ath6kl: rename vif init and cleanup functions
[cascardo/linux.git] / drivers / net / wireless / ath / ath6kl / cfg80211.c
index 6c59a21..7e92dc9 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <linux/moduleparam.h>
+#include <linux/inetdevice.h>
 
 #include "core.h"
 #include "cfg80211.h"
@@ -461,13 +462,13 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
                }
        }
 
-       if (sme->ie && (sme->ie_len > 0)) {
-               status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
-               if (status) {
-                       up(&ar->sem);
-                       return status;
-               }
-       } else
+       status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
+       if (status) {
+               up(&ar->sem);
+               return status;
+       }
+
+       if (sme->ie == NULL || sme->ie_len == 0)
                ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
 
        if (test_bit(CONNECTED, &vif->flags) &&
@@ -523,8 +524,7 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
            (vif->prwise_crypto == WEP_CRYPT)) {
                struct ath6kl_key *key = NULL;
 
-               if (sme->key_idx < WMI_MIN_KEY_INDEX ||
-                   sme->key_idx > WMI_MAX_KEY_INDEX) {
+               if (sme->key_idx > WMI_MAX_KEY_INDEX) {
                        ath6kl_err("key index %d out of bounds\n",
                                   sme->key_idx);
                        up(&ar->sem);
@@ -605,11 +605,13 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
        return 0;
 }
 
-static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
-                                   enum network_type nw_type,
-                                   const u8 *bssid,
-                                   struct ieee80211_channel *chan,
-                                   const u8 *beacon_ie, size_t beacon_ie_len)
+static struct cfg80211_bss *
+ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
+                        enum network_type nw_type,
+                        const u8 *bssid,
+                        struct ieee80211_channel *chan,
+                        const u8 *beacon_ie,
+                        size_t beacon_ie_len)
 {
        struct ath6kl *ar = vif->ar;
        struct cfg80211_bss *bss;
@@ -638,7 +640,7 @@ static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
                 */
                ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
                if (ie == NULL)
-                       return -ENOMEM;
+                       return NULL;
                ie[0] = WLAN_EID_SSID;
                ie[1] = vif->ssid_len;
                memcpy(ie + 2, vif->ssid, vif->ssid_len);
@@ -652,15 +654,9 @@ static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
                                   "cfg80211\n", bssid);
                kfree(ie);
        } else
-               ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss "
-                          "entry\n");
+               ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
 
-       if (bss == NULL)
-               return -ENOMEM;
-
-       cfg80211_put_bss(bss);
-
-       return 0;
+       return bss;
 }
 
 void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
@@ -672,6 +668,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
 {
        struct ieee80211_channel *chan;
        struct ath6kl *ar = vif->ar;
+       struct cfg80211_bss *bss;
 
        /* capinfo + listen interval */
        u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
@@ -712,8 +709,9 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
 
        chan = ieee80211_get_channel(ar->wiphy, (int) channel);
 
-       if (ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan, assoc_info,
-                                    beacon_ie_len) < 0) {
+       bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan,
+                                      assoc_info, beacon_ie_len);
+       if (!bss) {
                ath6kl_err("could not add cfg80211 bss entry\n");
                return;
        }
@@ -722,6 +720,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
                ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
                           nw_type & ADHOC_CREATOR ? "creator" : "joiner");
                cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
+               cfg80211_put_bss(bss);
                return;
        }
 
@@ -732,11 +731,11 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
                                        assoc_req_ie, assoc_req_len,
                                        assoc_resp_ie, assoc_resp_len,
                                        WLAN_STATUS_SUCCESS, GFP_KERNEL);
+               cfg80211_put_bss(bss);
        } else if (vif->sme_state == SME_CONNECTED) {
                /* inform roam event to cfg80211 */
-               cfg80211_roamed(vif->ndev, chan, bssid,
-                               assoc_req_ie, assoc_req_len,
-                               assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
+               cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len,
+                                   assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
        }
 }
 
@@ -984,6 +983,7 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
        struct ath6kl *ar = ath6kl_priv(ndev);
        struct ath6kl_vif *vif = netdev_priv(ndev);
        struct ath6kl_key *key = NULL;
+       int seq_len;
        u8 key_usage;
        u8 key_type;
 
@@ -997,7 +997,7 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
                                              params->key);
        }
 
-       if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+       if (key_index > WMI_MAX_KEY_INDEX) {
                ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
                           "%s: key index %d out of bounds\n", __func__,
                           key_index);
@@ -1012,23 +1012,21 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
        else
                key_usage = GROUP_USAGE;
 
-       if (params) {
-               int seq_len = params->seq_len;
-               if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
-                   seq_len > ATH6KL_KEY_SEQ_LEN) {
-                       /* Only first half of the WPI PN is configured */
-                       seq_len = ATH6KL_KEY_SEQ_LEN;
-               }
-               if (params->key_len > WLAN_MAX_KEY_LEN ||
-                   seq_len > sizeof(key->seq))
-                       return -EINVAL;
-
-               key->key_len = params->key_len;
-               memcpy(key->key, params->key, key->key_len);
-               key->seq_len = seq_len;
-               memcpy(key->seq, params->seq, key->seq_len);
-               key->cipher = params->cipher;
+       seq_len = params->seq_len;
+       if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
+           seq_len > ATH6KL_KEY_SEQ_LEN) {
+               /* Only first half of the WPI PN is configured */
+               seq_len = ATH6KL_KEY_SEQ_LEN;
        }
+       if (params->key_len > WLAN_MAX_KEY_LEN ||
+           seq_len > sizeof(key->seq))
+               return -EINVAL;
+
+       key->key_len = params->key_len;
+       memcpy(key->key, params->key, key->key_len);
+       key->seq_len = seq_len;
+       memcpy(key->seq, params->seq, key->seq_len);
+       key->cipher = params->cipher;
 
        switch (key->cipher) {
        case WLAN_CIPHER_SUITE_WEP40:
@@ -1115,7 +1113,7 @@ static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
        if (!ath6kl_cfg80211_ready(vif))
                return -EIO;
 
-       if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+       if (key_index > WMI_MAX_KEY_INDEX) {
                ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
                           "%s: key index %d out of bounds\n", __func__,
                           key_index);
@@ -1148,7 +1146,7 @@ static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
        if (!ath6kl_cfg80211_ready(vif))
                return -EIO;
 
-       if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+       if (key_index > WMI_MAX_KEY_INDEX) {
                ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
                           "%s: key index %d out of bounds\n", __func__,
                           key_index);
@@ -1184,7 +1182,7 @@ static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
        if (!ath6kl_cfg80211_ready(vif))
                return -EIO;
 
-       if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+       if (key_index > WMI_MAX_KEY_INDEX) {
                ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
                           "%s: key index %d out of bounds\n",
                           __func__, key_index);
@@ -1403,7 +1401,7 @@ static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
 
        ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
 
-       ath6kl_deinit_if_data(vif);
+       ath6kl_cfg80211_vif_cleanup(vif);
 
        return 0;
 }
@@ -1730,11 +1728,14 @@ static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
 
 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, pos, left;
        u32 filter = 0;
        u16 i;
-       u8 mask[WOW_MASK_SIZE];
+       u8 mask[WOW_MASK_SIZE], index = 0;
+       __be32 ips[MAX_IP_ADDRS];
 
        vif = ath6kl_vif_first(ar);
        if (!vif)
@@ -1781,6 +1782,33 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
                        return ret;
        }
 
+       /* Setup own IP addr for ARP agent. */
+       in_dev = __in_dev_get_rtnl(vif->ndev);
+       if (!in_dev)
+               goto skip_arp;
+
+       ifa = in_dev->ifa_list;
+       memset(&ips, 0, sizeof(ips));
+
+       /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
+       while (index < MAX_IP_ADDRS && ifa) {
+               ips[index] = ifa->ifa_local;
+               ifa = ifa->ifa_next;
+               index++;
+       }
+
+       if (ifa) {
+               ath6kl_err("total IP addr count is exceeding fw limit\n");
+               return -EINVAL;
+       }
+
+       ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
+       if (ret) {
+               ath6kl_err("fail to setup ip for arp agent\n");
+               return ret;
+       }
+
+skip_arp:
        if (wow->disconnect)
                filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
 
@@ -2014,7 +2042,18 @@ 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 = netdev_priv(dev);
+       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;
@@ -2214,6 +2253,11 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
        p.dot11_auth_mode = vif->dot11_auth_mode;
        p.ch = cpu_to_le16(vif->next_chan);
 
+       /* Enable uAPSD support by default */
+       res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
+       if (res < 0)
+               return res;
+
        if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
                p.nw_subtype = SUBTYPE_P2PGO;
        } else {
@@ -2259,6 +2303,19 @@ static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
        return 0;
 }
 
+static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
+                             u8 *mac)
+{
+       struct ath6kl *ar = ath6kl_priv(dev);
+       struct ath6kl_vif *vif = netdev_priv(dev);
+       const u8 *addr = mac ? mac : bcast_addr;
+
+       return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
+                                     addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+}
+
 static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
                                 u8 *mac, struct station_parameters *params)
 {
@@ -2518,6 +2575,12 @@ ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
                .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
                BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
        },
+       [NL80211_IFTYPE_AP] = {
+               .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       },
        [NL80211_IFTYPE_P2P_CLIENT] = {
                .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
                BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
@@ -2562,6 +2625,7 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = {
        .add_beacon = ath6kl_add_beacon,
        .set_beacon = ath6kl_set_beacon,
        .del_beacon = ath6kl_del_beacon,
+       .del_station = ath6kl_del_station,
        .change_station = ath6kl_change_station,
        .remain_on_channel = ath6kl_remain_on_channel,
        .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
@@ -2666,8 +2730,7 @@ struct ath6kl *ath6kl_core_alloc(struct device *dev)
        clear_bit(SKIP_SCAN, &ar->flag);
        clear_bit(DESTROY_IN_PROGRESS, &ar->flag);
 
-       ar->listen_intvl_t = A_DEFAULT_LISTEN_INTERVAL;
-       ar->listen_intvl_b = 0;
+       ar->listen_intvl_b = A_DEFAULT_LISTEN_INTERVAL;
        ar->tx_pwr = 0;
 
        ar->intra_bss = 1;
@@ -2682,6 +2745,7 @@ struct ath6kl *ath6kl_core_alloc(struct device *dev)
        for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) {
                spin_lock_init(&ar->sta_list[ctr].psq_lock);
                skb_queue_head_init(&ar->sta_list[ctr].psq);
+               skb_queue_head_init(&ar->sta_list[ctr].apsdq);
        }
 
        skb_queue_head_init(&ar->mcastpsq);
@@ -2742,7 +2806,7 @@ int ath6kl_register_ieee80211_hw(struct ath6kl *ar)
        return 0;
 }
 
-static int ath6kl_init_if_data(struct ath6kl_vif *vif)
+static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
 {
        vif->aggr_cntxt = aggr_init(vif->ndev);
        if (!vif->aggr_cntxt) {
@@ -2758,12 +2822,15 @@ static int ath6kl_init_if_data(struct ath6kl_vif *vif)
        set_bit(WMM_ENABLED, &vif->flags);
        spin_lock_init(&vif->if_lock);
 
+       INIT_LIST_HEAD(&vif->mc_filter);
+
        return 0;
 }
 
-void ath6kl_deinit_if_data(struct ath6kl_vif *vif)
+void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
 {
        struct ath6kl *ar = vif->ar;
+       struct ath6kl_mc_filter *mc_filter, *tmp;
 
        aggr_module_destroy(vif->aggr_cntxt);
 
@@ -2772,6 +2839,11 @@ void ath6kl_deinit_if_data(struct ath6kl_vif *vif)
        if (vif->nw_type == ADHOC_NETWORK)
                ar->ibss_if_active = false;
 
+       list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
+               list_del(&mc_filter->list);
+               kfree(mc_filter);
+       }
+
        unregister_netdevice(vif->ndev);
 
        ar->num_vif--;
@@ -2808,8 +2880,7 @@ struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
 
        ath6kl_init_control_info(vif);
 
-       /* TODO: Pass interface specific pointer instead of ar */
-       if (ath6kl_init_if_data(vif))
+       if (ath6kl_cfg80211_vif_init(vif))
                goto err;
 
        if (register_netdevice(ndev))