sdmmc: In order to improve the timing,set HOLD_REG to 1,mainly used in RK2928 and...
[firefly-linux-kernel-4.4.55.git] / net / mac80211 / cfg.c
index 2095602dcc3adc102836b8fbbf382c64b111f313..be70c70d3f5bfa6ecfb57e56d3b12eb06e6f78e9 100644 (file)
 #include "rate.h"
 #include "mesh.h"
 
-static bool nl80211_type_check(enum nl80211_iftype type)
-{
-       switch (type) {
-       case NL80211_IFTYPE_ADHOC:
-       case NL80211_IFTYPE_STATION:
-       case NL80211_IFTYPE_MONITOR:
-#ifdef CONFIG_MAC80211_MESH
-       case NL80211_IFTYPE_MESH_POINT:
-#endif
-       case NL80211_IFTYPE_AP:
-       case NL80211_IFTYPE_AP_VLAN:
-       case NL80211_IFTYPE_WDS:
-               return true;
-       default:
-               return false;
-       }
-}
-
-static bool nl80211_params_check(enum nl80211_iftype type,
-                                struct vif_params *params)
-{
-       if (!nl80211_type_check(type))
-               return false;
-
-       return true;
-}
-
-static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
-                              enum nl80211_iftype type, u32 *flags,
-                              struct vif_params *params)
+static struct net_device *ieee80211_add_iface(struct wiphy *wiphy, char *name,
+                                             enum nl80211_iftype type,
+                                             u32 *flags,
+                                             struct vif_params *params)
 {
        struct ieee80211_local *local = wiphy_priv(wiphy);
        struct net_device *dev;
        struct ieee80211_sub_if_data *sdata;
        int err;
 
-       if (!nl80211_params_check(type, params))
-               return -EINVAL;
-
        err = ieee80211_if_add(local, name, &dev, type, params);
-       if (err || type != NL80211_IFTYPE_MONITOR || !flags)
-               return err;
+       if (err)
+               return ERR_PTR(err);
 
-       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       sdata->u.mntr_flags = *flags;
-       return 0;
+       if (type == NL80211_IFTYPE_MONITOR && flags) {
+               sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+               sdata->u.mntr_flags = *flags;
+       }
+
+       return dev;
 }
 
 static int ieee80211_del_iface(struct wiphy *wiphy, struct net_device *dev)
@@ -82,21 +56,10 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        int ret;
 
-       if (ieee80211_sdata_running(sdata))
-               return -EBUSY;
-
-       if (!nl80211_params_check(type, params))
-               return -EINVAL;
-
        ret = ieee80211_if_change_type(sdata, type);
        if (ret)
                return ret;
 
-       if (ieee80211_vif_is_mesh(&sdata->vif) && params->mesh_id_len)
-               ieee80211_sdata_set_mesh_id(sdata,
-                                           params->mesh_id_len,
-                                           params->mesh_id);
-
        if (type == NL80211_IFTYPE_AP_VLAN &&
            params && params->use_4addr == 0)
                rcu_assign_pointer(sdata->u.vlan.sta, NULL);
@@ -104,59 +67,79 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
                 params && params->use_4addr >= 0)
                sdata->u.mgd.use_4addr = params->use_4addr;
 
-       if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags)
-               sdata->u.mntr_flags = *flags;
+       if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags) {
+               struct ieee80211_local *local = sdata->local;
+
+               if (ieee80211_sdata_running(sdata)) {
+                       /*
+                        * Prohibit MONITOR_FLAG_COOK_FRAMES to be
+                        * changed while the interface is up.
+                        * Else we would need to add a lot of cruft
+                        * to update everything:
+                        *      cooked_mntrs, monitor and all fif_* counters
+                        *      reconfigure hardware
+                        */
+                       if ((*flags & MONITOR_FLAG_COOK_FRAMES) !=
+                           (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES))
+                               return -EBUSY;
+
+                       ieee80211_adjust_monitor_flags(sdata, -1);
+                       sdata->u.mntr_flags = *flags;
+                       ieee80211_adjust_monitor_flags(sdata, 1);
+
+                       ieee80211_configure_filter(local);
+               } else {
+                       /*
+                        * Because the interface is down, ieee80211_do_stop
+                        * and ieee80211_do_open take care of "everything"
+                        * mentioned in the comment above.
+                        */
+                       sdata->u.mntr_flags = *flags;
+               }
+       }
 
        return 0;
 }
 
 static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
-                            u8 key_idx, const u8 *mac_addr,
+                            u8 key_idx, bool pairwise, const u8 *mac_addr,
                             struct key_params *params)
 {
-       struct ieee80211_sub_if_data *sdata;
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct sta_info *sta = NULL;
-       enum ieee80211_key_alg alg;
        struct ieee80211_key *key;
        int err;
 
-       if (!netif_running(dev))
+       if (!ieee80211_sdata_running(sdata))
                return -ENETDOWN;
 
-       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
+       /* reject WEP and TKIP keys if WEP failed to initialize */
        switch (params->cipher) {
        case WLAN_CIPHER_SUITE_WEP40:
-       case WLAN_CIPHER_SUITE_WEP104:
-               alg = ALG_WEP;
-               break;
        case WLAN_CIPHER_SUITE_TKIP:
-               alg = ALG_TKIP;
-               break;
-       case WLAN_CIPHER_SUITE_CCMP:
-               alg = ALG_CCMP;
-               break;
-       case WLAN_CIPHER_SUITE_AES_CMAC:
-               alg = ALG_AES_CMAC;
+       case WLAN_CIPHER_SUITE_WEP104:
+               if (IS_ERR(sdata->local->wep_tx_tfm))
+                       return -EINVAL;
                break;
        default:
-               return -EINVAL;
+               break;
        }
 
-       /* reject WEP and TKIP keys if WEP failed to initialize */
-       if ((alg == ALG_WEP || alg == ALG_TKIP) &&
-           IS_ERR(sdata->local->wep_tx_tfm))
-               return -EINVAL;
+       key = ieee80211_key_alloc(params->cipher, key_idx, params->key_len,
+                                 params->key, params->seq_len, params->seq);
+       if (IS_ERR(key))
+               return PTR_ERR(key);
 
-       key = ieee80211_key_alloc(alg, key_idx, params->key_len, params->key,
-                                 params->seq_len, params->seq);
-       if (!key)
-               return -ENOMEM;
+       if (pairwise)
+               key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
 
        mutex_lock(&sdata->local->sta_mtx);
 
        if (mac_addr) {
-               sta = sta_info_get_bss(sdata, mac_addr);
+               if (ieee80211_vif_is_mesh(&sdata->vif))
+                       sta = sta_info_get(sdata, mac_addr);
+               else
+                       sta = sta_info_get_bss(sdata, mac_addr);
                if (!sta) {
                        ieee80211_key_free(sdata->local, key);
                        err = -ENOENT;
@@ -164,9 +147,10 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
                }
        }
 
-       ieee80211_key_link(key, sdata, sta);
+       err = ieee80211_key_link(key, sdata, sta);
+       if (err)
+               ieee80211_key_free(sdata->local, key);
 
-       err = 0;
  out_unlock:
        mutex_unlock(&sdata->local->sta_mtx);
 
@@ -174,15 +158,16 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
-                            u8 key_idx, const u8 *mac_addr)
+                            u8 key_idx, bool pairwise, const u8 *mac_addr)
 {
-       struct ieee80211_sub_if_data *sdata;
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
        struct sta_info *sta;
+       struct ieee80211_key *key = NULL;
        int ret;
 
-       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
-       mutex_lock(&sdata->local->sta_mtx);
+       mutex_lock(&local->sta_mtx);
+       mutex_lock(&local->key_mtx);
 
        if (mac_addr) {
                ret = -ENOENT;
@@ -191,32 +176,31 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
                if (!sta)
                        goto out_unlock;
 
-               if (sta->key) {
-                       ieee80211_key_free(sdata->local, sta->key);
-                       WARN_ON(sta->key);
-                       ret = 0;
-               }
-
-               goto out_unlock;
-       }
+               if (pairwise)
+                       key = key_mtx_dereference(local, sta->ptk);
+               else
+                       key = key_mtx_dereference(local, sta->gtk[key_idx]);
+       } else
+               key = key_mtx_dereference(local, sdata->keys[key_idx]);
 
-       if (!sdata->keys[key_idx]) {
+       if (!key) {
                ret = -ENOENT;
                goto out_unlock;
        }
 
-       ieee80211_key_free(sdata->local, sdata->keys[key_idx]);
-       WARN_ON(sdata->keys[key_idx]);
+       __ieee80211_key_free(key);
 
        ret = 0;
  out_unlock:
-       mutex_unlock(&sdata->local->sta_mtx);
+       mutex_unlock(&local->key_mtx);
+       mutex_unlock(&local->sta_mtx);
 
        return ret;
 }
 
 static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
-                            u8 key_idx, const u8 *mac_addr, void *cookie,
+                            u8 key_idx, bool pairwise, const u8 *mac_addr,
+                            void *cookie,
                             void (*callback)(void *cookie,
                                              struct key_params *params))
 {
@@ -224,7 +208,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
        struct sta_info *sta = NULL;
        u8 seq[6] = {0};
        struct key_params params;
-       struct ieee80211_key *key;
+       struct ieee80211_key *key = NULL;
        u32 iv32;
        u16 iv16;
        int err = -ENOENT;
@@ -238,19 +222,22 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
                if (!sta)
                        goto out;
 
-               key = sta->key;
+               if (pairwise)
+                       key = rcu_dereference(sta->ptk);
+               else if (key_idx < NUM_DEFAULT_KEYS)
+                       key = rcu_dereference(sta->gtk[key_idx]);
        } else
-               key = sdata->keys[key_idx];
+               key = rcu_dereference(sdata->keys[key_idx]);
 
        if (!key)
                goto out;
 
        memset(&params, 0, sizeof(params));
 
-       switch (key->conf.alg) {
-       case ALG_TKIP:
-               params.cipher = WLAN_CIPHER_SUITE_TKIP;
+       params.cipher = key->conf.cipher;
 
+       switch (key->conf.cipher) {
+       case WLAN_CIPHER_SUITE_TKIP:
                iv32 = key->u.tkip.tx.iv32;
                iv16 = key->u.tkip.tx.iv16;
 
@@ -268,8 +255,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
                params.seq = seq;
                params.seq_len = 6;
                break;
-       case ALG_CCMP:
-               params.cipher = WLAN_CIPHER_SUITE_CCMP;
+       case WLAN_CIPHER_SUITE_CCMP:
                seq[0] = key->u.ccmp.tx_pn[5];
                seq[1] = key->u.ccmp.tx_pn[4];
                seq[2] = key->u.ccmp.tx_pn[3];
@@ -279,14 +265,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
                params.seq = seq;
                params.seq_len = 6;
                break;
-       case ALG_WEP:
-               if (key->conf.keylen == 5)
-                       params.cipher = WLAN_CIPHER_SUITE_WEP40;
-               else
-                       params.cipher = WLAN_CIPHER_SUITE_WEP104;
-               break;
-       case ALG_AES_CMAC:
-               params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
+       case WLAN_CIPHER_SUITE_AES_CMAC:
                seq[0] = key->u.aes_cmac.tx_pn[5];
                seq[1] = key->u.aes_cmac.tx_pn[4];
                seq[2] = key->u.aes_cmac.tx_pn[3];
@@ -311,11 +290,12 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
 
 static int ieee80211_config_default_key(struct wiphy *wiphy,
                                        struct net_device *dev,
-                                       u8 key_idx)
+                                       u8 key_idx, bool uni,
+                                       bool multi)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       ieee80211_set_default_key(sdata, key_idx);
+       ieee80211_set_default_key(sdata, key_idx, uni, multi);
 
        return 0;
 }
@@ -331,9 +311,21 @@ static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy,
        return 0;
 }
 
+static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, int idx)
+{
+       if (!(rate->flags & RATE_INFO_FLAGS_MCS)) {
+               struct ieee80211_supported_band *sband;
+               sband = sta->local->hw.wiphy->bands[
+                               sta->local->hw.conf.channel->band];
+               rate->legacy = sband->bitrates[idx].bitrate;
+       } else
+               rate->mcs = idx;
+}
+
 static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
+       struct timespec uptime;
 
        sinfo->generation = sdata->local->sta_generation;
 
@@ -342,18 +334,31 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                        STATION_INFO_TX_BYTES |
                        STATION_INFO_RX_PACKETS |
                        STATION_INFO_TX_PACKETS |
-                       STATION_INFO_TX_BITRATE;
+                       STATION_INFO_TX_RETRIES |
+                       STATION_INFO_TX_FAILED |
+                       STATION_INFO_TX_BITRATE |
+                       STATION_INFO_RX_BITRATE |
+                       STATION_INFO_RX_DROP_MISC |
+                       STATION_INFO_BSS_PARAM |
+                       STATION_INFO_CONNECTED_TIME;
+
+       do_posix_clock_monotonic_gettime(&uptime);
+       sinfo->connected_time = uptime.tv_sec - sta->last_connected;
 
        sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
        sinfo->rx_bytes = sta->rx_bytes;
        sinfo->tx_bytes = sta->tx_bytes;
        sinfo->rx_packets = sta->rx_packets;
        sinfo->tx_packets = sta->tx_packets;
+       sinfo->tx_retries = sta->tx_retry_count;
+       sinfo->tx_failed = sta->tx_retry_failed;
+       sinfo->rx_dropped_misc = sta->rx_dropped;
 
        if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
            (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
-               sinfo->filled |= STATION_INFO_SIGNAL;
+               sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
                sinfo->signal = (s8)sta->last_signal;
+               sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
        }
 
        sinfo->txrate.flags = 0;
@@ -363,15 +368,16 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
        if (sta->last_tx_rate.flags & IEEE80211_TX_RC_SHORT_GI)
                sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+       rate_idx_to_bitrate(&sinfo->txrate, sta, sta->last_tx_rate.idx);
 
-       if (!(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)) {
-               struct ieee80211_supported_band *sband;
-               sband = sta->local->hw.wiphy->bands[
-                               sta->local->hw.conf.channel->band];
-               sinfo->txrate.legacy =
-                       sband->bitrates[sta->last_tx_rate.idx].bitrate;
-       } else
-               sinfo->txrate.mcs = sta->last_tx_rate.idx;
+       sinfo->rxrate.flags = 0;
+       if (sta->last_rx_rate_flag & RX_FLAG_HT)
+               sinfo->rxrate.flags |= RATE_INFO_FLAGS_MCS;
+       if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
+               sinfo->rxrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+       if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
+               sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+       rate_idx_to_bitrate(&sinfo->rxrate, sta, sta->last_rx_rate_idx);
 
        if (ieee80211_vif_is_mesh(&sdata->vif)) {
 #ifdef CONFIG_MAC80211_MESH
@@ -384,6 +390,16 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                sinfo->plink_state = sta->plink_state;
 #endif
        }
+
+       sinfo->bss_param.flags = 0;
+       if (sdata->vif.bss_conf.use_cts_prot)
+               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
+       if (sdata->vif.bss_conf.use_short_preamble)
+               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
+       if (sdata->vif.bss_conf.use_short_slot)
+               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
+       sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period;
+       sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int;
 }
 
 
@@ -447,7 +463,7 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
        int size;
        int err = -EINVAL;
 
-       old = sdata->u.ap.beacon;
+       old = rtnl_dereference(sdata->u.ap.beacon);
 
        /* head must not be zero-length */
        if (params->head && !params->head_len)
@@ -542,8 +558,7 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       old = sdata->u.ap.beacon;
-
+       old = rtnl_dereference(sdata->u.ap.beacon);
        if (old)
                return -EALREADY;
 
@@ -558,8 +573,7 @@ static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev,
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       old = sdata->u.ap.beacon;
-
+       old = rtnl_dereference(sdata->u.ap.beacon);
        if (!old)
                return -ENOENT;
 
@@ -573,8 +587,7 @@ static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev)
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       old = sdata->u.ap.beacon;
-
+       old = rtnl_dereference(sdata->u.ap.beacon);
        if (!old)
                return -ENOENT;
 
@@ -670,6 +683,12 @@ static void sta_apply_parameters(struct ieee80211_local *local,
                if (set & BIT(NL80211_STA_FLAG_MFP))
                        sta->flags |= WLAN_STA_MFP;
        }
+
+       if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
+               sta->flags &= ~WLAN_STA_AUTH;
+               if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
+                       sta->flags |= WLAN_STA_AUTH;
+       }
        spin_unlock_irqrestore(&sta->flaglock, flags);
 
        /*
@@ -707,15 +726,29 @@ static void sta_apply_parameters(struct ieee80211_local *local,
                                                  params->ht_capa,
                                                  &sta->sta.ht_cap);
 
-       if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) {
-               switch (params->plink_action) {
-               case PLINK_ACTION_OPEN:
-                       mesh_plink_open(sta);
-                       break;
-               case PLINK_ACTION_BLOCK:
-                       mesh_plink_block(sta);
-                       break;
-               }
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+#ifdef CONFIG_MAC80211_MESH
+               if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED)
+                       switch (params->plink_state) {
+                       case NL80211_PLINK_LISTEN:
+                       case NL80211_PLINK_ESTAB:
+                       case NL80211_PLINK_BLOCKED:
+                               sta->plink_state = params->plink_state;
+                               break;
+                       default:
+                               /*  nothing  */
+                               break;
+                       }
+               else
+                       switch (params->plink_action) {
+                       case PLINK_ACTION_OPEN:
+                               mesh_plink_open(sta);
+                               break;
+                       case PLINK_ACTION_BLOCK:
+                               mesh_plink_block(sta);
+                               break;
+                       }
+#endif
        }
 }
 
@@ -829,6 +862,10 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 
        rcu_read_unlock();
 
+       if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+           params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))
+               ieee80211_recalc_ps(local, -1);
+
        return 0;
 }
 
@@ -912,8 +949,10 @@ static int ieee80211_change_mpath(struct wiphy *wiphy,
 static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
                            struct mpath_info *pinfo)
 {
-       if (mpath->next_hop)
-               memcpy(next_hop, mpath->next_hop->sta.addr, ETH_ALEN);
+       struct sta_info *next_hop_sta = rcu_dereference(mpath->next_hop);
+
+       if (next_hop_sta)
+               memcpy(next_hop, next_hop_sta->sta.addr, ETH_ALEN);
        else
                memset(next_hop, 0, ETH_ALEN);
 
@@ -992,7 +1031,7 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
        return 0;
 }
 
-static int ieee80211_get_mesh_params(struct wiphy *wiphy,
+static int ieee80211_get_mesh_config(struct wiphy *wiphy,
                                struct net_device *dev,
                                struct mesh_config *conf)
 {
@@ -1008,9 +1047,43 @@ static inline bool _chg_mesh_attr(enum nl80211_meshconf_params parm, u32 mask)
        return (mask >> (parm-1)) & 0x1;
 }
 
-static int ieee80211_set_mesh_params(struct wiphy *wiphy,
-                               struct net_device *dev,
-                               const struct mesh_config *nconf, u32 mask)
+static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
+               const struct mesh_setup *setup)
+{
+       u8 *new_ie;
+       const u8 *old_ie;
+
+       /* allocate information elements */
+       new_ie = NULL;
+       old_ie = ifmsh->ie;
+
+       if (setup->ie_len) {
+               new_ie = kmemdup(setup->ie, setup->ie_len,
+                               GFP_KERNEL);
+               if (!new_ie)
+                       return -ENOMEM;
+       }
+       ifmsh->ie_len = setup->ie_len;
+       ifmsh->ie = new_ie;
+       kfree(old_ie);
+
+       /* now copy the rest of the setup parameters */
+       ifmsh->mesh_id_len = setup->mesh_id_len;
+       memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len);
+       ifmsh->mesh_pp_id = setup->path_sel_proto;
+       ifmsh->mesh_pm_id = setup->path_metric;
+       ifmsh->security = IEEE80211_MESH_SEC_NONE;
+       if (setup->is_authenticated)
+               ifmsh->security |= IEEE80211_MESH_SEC_AUTHED;
+       if (setup->is_secure)
+               ifmsh->security |= IEEE80211_MESH_SEC_SECURED;
+
+       return 0;
+}
+
+static int ieee80211_update_mesh_config(struct wiphy *wiphy,
+                                       struct net_device *dev, u32 mask,
+                                       const struct mesh_config *nconf)
 {
        struct mesh_config *conf;
        struct ieee80211_sub_if_data *sdata;
@@ -1033,6 +1106,8 @@ static int ieee80211_set_mesh_params(struct wiphy *wiphy,
                conf->dot11MeshMaxRetries = nconf->dot11MeshMaxRetries;
        if (_chg_mesh_attr(NL80211_MESHCONF_TTL, mask))
                conf->dot11MeshTTL = nconf->dot11MeshTTL;
+       if (_chg_mesh_attr(NL80211_MESHCONF_ELEMENT_TTL, mask))
+               conf->dot11MeshTTL = nconf->element_ttl;
        if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask))
                conf->auto_open_plinks = nconf->auto_open_plinks;
        if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, mask))
@@ -1059,6 +1134,31 @@ static int ieee80211_set_mesh_params(struct wiphy *wiphy,
        return 0;
 }
 
+static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
+                              const struct mesh_config *conf,
+                              const struct mesh_setup *setup)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+       int err;
+
+       memcpy(&ifmsh->mshcfg, conf, sizeof(struct mesh_config));
+       err = copy_mesh_setup(ifmsh, setup);
+       if (err)
+               return err;
+       ieee80211_start_mesh(sdata);
+
+       return 0;
+}
+
+static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       ieee80211_stop_mesh(sdata);
+
+       return 0;
+}
 #endif
 
 static int ieee80211_change_bss(struct wiphy *wiphy,
@@ -1117,6 +1217,12 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
                        sdata->flags &= ~IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
        }
 
+       if (params->ht_opmode >= 0) {
+               sdata->vif.bss_conf.ht_operation_mode =
+                       (u16) params->ht_opmode;
+               changed |= BSS_CHANGED_HT;
+       }
+
        ieee80211_bss_info_change_notify(sdata, changed);
 
        return 0;
@@ -1144,9 +1250,9 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy,
        p.uapsd = false;
 
        if (drv_conf_tx(local, params->queue, &p)) {
-               printk(KERN_DEBUG "%s: failed to set TX queue "
-                      "parameters for queue %d\n",
-                      wiphy_name(local->hw.wiphy), params->queue);
+               wiphy_debug(local->hw.wiphy,
+                           "failed to set TX queue parameters for queue %d\n",
+                           params->queue);
                return -EINVAL;
        }
 
@@ -1160,6 +1266,9 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
 {
        struct ieee80211_local *local = wiphy_priv(wiphy);
        struct ieee80211_sub_if_data *sdata = NULL;
+       struct ieee80211_channel *old_oper;
+       enum nl80211_channel_type old_oper_type;
+       enum nl80211_channel_type old_vif_oper_type= NL80211_CHAN_NO_HT;
 
        if (netdev)
                sdata = IEEE80211_DEV_TO_SUB_IF(netdev);
@@ -1177,22 +1286,33 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
                break;
        }
 
-       local->oper_channel = chan;
+       if (sdata)
+               old_vif_oper_type = sdata->vif.bss_conf.channel_type;
+       old_oper_type = local->_oper_channel_type;
 
        if (!ieee80211_set_channel_type(local, sdata, channel_type))
                return -EBUSY;
 
-       ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
-       if (sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR)
+       old_oper = local->oper_channel;
+       local->oper_channel = chan;
+
+       /* Update driver if changes were actually made. */
+       if ((old_oper != local->oper_channel) ||
+           (old_oper_type != local->_oper_channel_type))
+               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+
+       if ((sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR) &&
+           old_vif_oper_type != sdata->vif.bss_conf.channel_type)
                ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT);
 
        return 0;
 }
 
 #ifdef CONFIG_PM
-static int ieee80211_suspend(struct wiphy *wiphy)
+static int ieee80211_suspend(struct wiphy *wiphy,
+                            struct cfg80211_wowlan *wowlan)
 {
-       return __ieee80211_suspend(wiphy_priv(wiphy));
+       return __ieee80211_suspend(wiphy_priv(wiphy), wowlan);
 }
 
 static int ieee80211_resume(struct wiphy *wiphy)
@@ -1208,19 +1328,57 @@ static int ieee80211_scan(struct wiphy *wiphy,
                          struct net_device *dev,
                          struct cfg80211_scan_request *req)
 {
-       struct ieee80211_sub_if_data *sdata;
-
-       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->vif.type != NL80211_IFTYPE_STATION &&
-           sdata->vif.type != NL80211_IFTYPE_ADHOC &&
-           sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
-           (sdata->vif.type != NL80211_IFTYPE_AP || sdata->u.ap.beacon))
+       switch (ieee80211_vif_type_p2p(&sdata->vif)) {
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MESH_POINT:
+       case NL80211_IFTYPE_P2P_CLIENT:
+               break;
+       case NL80211_IFTYPE_P2P_GO:
+               if (sdata->local->ops->hw_scan)
+                       break;
+               /*
+                * FIXME: implement NoA while scanning in software,
+                * for now fall through to allow scanning only when
+                * beaconing hasn't been configured yet
+                */
+       case NL80211_IFTYPE_AP:
+               if (sdata->u.ap.beacon)
+                       return -EOPNOTSUPP;
+               break;
+       default:
                return -EOPNOTSUPP;
+       }
 
        return ieee80211_request_scan(sdata, req);
 }
 
+static int
+ieee80211_sched_scan_start(struct wiphy *wiphy,
+                          struct net_device *dev,
+                          struct cfg80211_sched_scan_request *req)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (!sdata->local->ops->sched_scan_start)
+               return -EOPNOTSUPP;
+
+       return ieee80211_request_sched_scan_start(sdata, req);
+}
+
+static int
+ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (!sdata->local->ops->sched_scan_stop)
+               return -EOPNOTSUPP;
+
+       return ieee80211_request_sched_scan_stop(sdata);
+}
+
 static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
                          struct cfg80211_auth_request *req)
 {
@@ -1297,6 +1455,13 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
        struct ieee80211_local *local = wiphy_priv(wiphy);
        int err;
 
+       if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
+               err = drv_set_frag_threshold(local, wiphy->frag_threshold);
+
+               if (err)
+                       return err;
+       }
+
        if (changed & WIPHY_PARAM_COVERAGE_CLASS) {
                err = drv_set_coverage_class(local, wiphy->coverage_class);
 
@@ -1363,7 +1528,7 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm)
 }
 
 static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev,
-                                 u8 *addr)
+                                 const u8 *addr)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -1398,6 +1563,8 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
        enum ieee80211_smps_mode old_req;
        int err;
 
+       lockdep_assert_held(&sdata->u.mgd.mtx);
+
        old_req = sdata->u.mgd.req_smps;
        sdata->u.mgd.req_smps = smps_mode;
 
@@ -1412,7 +1579,7 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
        if (!sdata->u.mgd.associated ||
            sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) {
                mutex_lock(&sdata->local->iflist_mtx);
-               ieee80211_recalc_smps(sdata->local, sdata);
+               ieee80211_recalc_smps(sdata->local);
                mutex_unlock(&sdata->local->iflist_mtx);
                return 0;
        }
@@ -1503,16 +1670,13 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-       int i;
-
-       /*
-        * This _could_ be supported by providing a hook for
-        * drivers for this function, but at this point it
-        * doesn't seem worth bothering.
-        */
-       if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
-               return -EOPNOTSUPP;
+       int i, ret;
 
+       if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) {
+               ret = drv_set_bitrate_mask(local, sdata, mask);
+               if (ret)
+                       return ret;
+       }
 
        for (i = 0; i < IEEE80211_NUM_BANDS; i++)
                sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
@@ -1520,6 +1684,37 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
        return 0;
 }
 
+static int ieee80211_remain_on_channel_hw(struct ieee80211_local *local,
+                                         struct net_device *dev,
+                                         struct ieee80211_channel *chan,
+                                         enum nl80211_channel_type chantype,
+                                         unsigned int duration, u64 *cookie)
+{
+       int ret;
+       u32 random_cookie;
+
+       lockdep_assert_held(&local->mtx);
+
+       if (local->hw_roc_cookie)
+               return -EBUSY;
+       /* must be nonzero */
+       random_cookie = random32() | 1;
+
+       *cookie = random_cookie;
+       local->hw_roc_dev = dev;
+       local->hw_roc_cookie = random_cookie;
+       local->hw_roc_channel = chan;
+       local->hw_roc_channel_type = chantype;
+       local->hw_roc_duration = duration;
+       ret = drv_remain_on_channel(local, chan, chantype, duration);
+       if (ret) {
+               local->hw_roc_channel = NULL;
+               local->hw_roc_cookie = 0;
+       }
+
+       return ret;
+}
+
 static int ieee80211_remain_on_channel(struct wiphy *wiphy,
                                       struct net_device *dev,
                                       struct ieee80211_channel *chan,
@@ -1528,46 +1723,131 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy,
                                       u64 *cookie)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+
+       if (local->ops->remain_on_channel) {
+               int ret;
+
+               mutex_lock(&local->mtx);
+               ret = ieee80211_remain_on_channel_hw(local, dev,
+                                                    chan, channel_type,
+                                                    duration, cookie);
+               local->hw_roc_for_tx = false;
+               mutex_unlock(&local->mtx);
+
+               return ret;
+       }
 
        return ieee80211_wk_remain_on_channel(sdata, chan, channel_type,
                                              duration, cookie);
 }
 
+static int ieee80211_cancel_remain_on_channel_hw(struct ieee80211_local *local,
+                                                u64 cookie)
+{
+       int ret;
+
+       lockdep_assert_held(&local->mtx);
+
+       if (local->hw_roc_cookie != cookie)
+               return -ENOENT;
+
+       ret = drv_cancel_remain_on_channel(local);
+       if (ret)
+               return ret;
+
+       local->hw_roc_cookie = 0;
+       local->hw_roc_channel = NULL;
+
+       ieee80211_recalc_idle(local);
+
+       return 0;
+}
+
 static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
                                              struct net_device *dev,
                                              u64 cookie)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+
+       if (local->ops->cancel_remain_on_channel) {
+               int ret;
+
+               mutex_lock(&local->mtx);
+               ret = ieee80211_cancel_remain_on_channel_hw(local, cookie);
+               mutex_unlock(&local->mtx);
+
+               return ret;
+       }
 
        return ieee80211_wk_cancel_remain_on_channel(sdata, cookie);
 }
 
-static int ieee80211_action(struct wiphy *wiphy, struct net_device *dev,
-                           struct ieee80211_channel *chan,
-                           enum nl80211_channel_type channel_type,
-                           bool channel_type_valid,
-                           const u8 *buf, size_t len, u64 *cookie)
+static enum work_done_result
+ieee80211_offchan_tx_done(struct ieee80211_work *wk, struct sk_buff *skb)
+{
+       /*
+        * Use the data embedded in the work struct for reporting
+        * here so if the driver mangled the SKB before dropping
+        * it (which is the only way we really should get here)
+        * then we don't report mangled data.
+        *
+        * If there was no wait time, then by the time we get here
+        * the driver will likely not have reported the status yet,
+        * so in that case userspace will have to deal with it.
+        */
+
+       if (wk->offchan_tx.wait && wk->offchan_tx.frame)
+               cfg80211_mgmt_tx_status(wk->sdata->dev,
+                                       (unsigned long) wk->offchan_tx.frame,
+                                       wk->ie, wk->ie_len, false, GFP_KERNEL);
+
+       return WORK_DONE_DESTROY;
+}
+
+static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
+                            struct ieee80211_channel *chan, bool offchan,
+                            enum nl80211_channel_type channel_type,
+                            bool channel_type_valid, unsigned int wait,
+                            const u8 *buf, size_t len, u64 *cookie)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb;
        struct sta_info *sta;
+       struct ieee80211_work *wk;
        const struct ieee80211_mgmt *mgmt = (void *)buf;
        u32 flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
                    IEEE80211_TX_CTL_REQ_TX_STATUS;
+       bool is_offchan = false;
 
        /* Check that we are on the requested channel for transmission */
        if (chan != local->tmp_channel &&
            chan != local->oper_channel)
-               return -EBUSY;
+               is_offchan = true;
        if (channel_type_valid &&
            (channel_type != local->tmp_channel_type &&
             channel_type != local->_oper_channel_type))
+               is_offchan = true;
+
+       if (chan == local->hw_roc_channel) {
+               /* TODO: check channel type? */
+               is_offchan = false;
+               flags |= IEEE80211_TX_CTL_TX_OFFCHAN;
+       }
+
+       if (is_offchan && !offchan)
                return -EBUSY;
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_ADHOC:
-               if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
+       case NL80211_IFTYPE_P2P_GO:
+       case NL80211_IFTYPE_MESH_POINT:
+               if (!ieee80211_is_action(mgmt->frame_control) ||
+                   mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)
                        break;
                rcu_read_lock();
                sta = sta_info_get(sdata, mgmt->da);
@@ -1576,8 +1856,7 @@ static int ieee80211_action(struct wiphy *wiphy, struct net_device *dev,
                        return -ENOLINK;
                break;
        case NL80211_IFTYPE_STATION:
-               if (!(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED))
-                       flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+       case NL80211_IFTYPE_P2P_CLIENT:
                break;
        default:
                return -EOPNOTSUPP;
@@ -1593,12 +1872,219 @@ static int ieee80211_action(struct wiphy *wiphy, struct net_device *dev,
        IEEE80211_SKB_CB(skb)->flags = flags;
 
        skb->dev = sdata->dev;
-       ieee80211_tx_skb(sdata, skb);
 
        *cookie = (unsigned long) skb;
+
+       if (is_offchan && local->ops->offchannel_tx) {
+               int ret;
+
+               IEEE80211_SKB_CB(skb)->band = chan->band;
+
+               mutex_lock(&local->mtx);
+
+               if (local->hw_offchan_tx_cookie) {
+                       mutex_unlock(&local->mtx);
+                       return -EBUSY;
+               }
+
+               /* TODO: bitrate control, TX processing? */
+               ret = drv_offchannel_tx(local, skb, chan, channel_type, wait);
+
+               if (ret == 0)
+                       local->hw_offchan_tx_cookie = *cookie;
+               mutex_unlock(&local->mtx);
+
+               /*
+                * Allow driver to return 1 to indicate it wants to have the
+                * frame transmitted with a remain_on_channel + regular TX.
+                */
+               if (ret != 1)
+                       return ret;
+       }
+
+       if (is_offchan && local->ops->remain_on_channel) {
+               unsigned int duration;
+               int ret;
+
+               mutex_lock(&local->mtx);
+               /*
+                * If the duration is zero, then the driver
+                * wouldn't actually do anything. Set it to
+                * 100 for now.
+                *
+                * TODO: cancel the off-channel operation
+                *       when we get the SKB's TX status and
+                *       the wait time was zero before.
+                */
+               duration = 100;
+               if (wait)
+                       duration = wait;
+               ret = ieee80211_remain_on_channel_hw(local, dev, chan,
+                                                    channel_type,
+                                                    duration, cookie);
+               if (ret) {
+                       kfree_skb(skb);
+                       mutex_unlock(&local->mtx);
+                       return ret;
+               }
+
+               local->hw_roc_for_tx = true;
+               local->hw_roc_duration = wait;
+
+               /*
+                * queue up frame for transmission after
+                * ieee80211_ready_on_channel call
+                */
+
+               /* modify cookie to prevent API mismatches */
+               *cookie ^= 2;
+               IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN;
+               local->hw_roc_skb = skb;
+               local->hw_roc_skb_for_status = skb;
+               mutex_unlock(&local->mtx);
+
+               return 0;
+       }
+
+       /*
+        * Can transmit right away if the channel was the
+        * right one and there's no wait involved... If a
+        * wait is involved, we might otherwise not be on
+        * the right channel for long enough!
+        */
+       if (!is_offchan && !wait && !sdata->vif.bss_conf.idle) {
+               ieee80211_tx_skb(sdata, skb);
+               return 0;
+       }
+
+       wk = kzalloc(sizeof(*wk) + len, GFP_KERNEL);
+       if (!wk) {
+               kfree_skb(skb);
+               return -ENOMEM;
+       }
+
+       wk->type = IEEE80211_WORK_OFFCHANNEL_TX;
+       wk->chan = chan;
+       wk->chan_type = channel_type;
+       wk->sdata = sdata;
+       wk->done = ieee80211_offchan_tx_done;
+       wk->offchan_tx.frame = skb;
+       wk->offchan_tx.wait = wait;
+       wk->ie_len = len;
+       memcpy(wk->ie, buf, len);
+
+       ieee80211_add_work(wk);
        return 0;
 }
 
+static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
+                                        struct net_device *dev,
+                                        u64 cookie)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_work *wk;
+       int ret = -ENOENT;
+
+       mutex_lock(&local->mtx);
+
+       if (local->ops->offchannel_tx_cancel_wait &&
+           local->hw_offchan_tx_cookie == cookie) {
+               ret = drv_offchannel_tx_cancel_wait(local);
+
+               if (!ret)
+                       local->hw_offchan_tx_cookie = 0;
+
+               mutex_unlock(&local->mtx);
+
+               return ret;
+       }
+
+       if (local->ops->cancel_remain_on_channel) {
+               cookie ^= 2;
+               ret = ieee80211_cancel_remain_on_channel_hw(local, cookie);
+
+               if (ret == 0) {
+                       kfree_skb(local->hw_roc_skb);
+                       local->hw_roc_skb = NULL;
+                       local->hw_roc_skb_for_status = NULL;
+               }
+
+               mutex_unlock(&local->mtx);
+
+               return ret;
+       }
+
+       list_for_each_entry(wk, &local->work_list, list) {
+               if (wk->sdata != sdata)
+                       continue;
+
+               if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX)
+                       continue;
+
+               if (cookie != (unsigned long) wk->offchan_tx.frame)
+                       continue;
+
+               wk->timeout = jiffies;
+
+               ieee80211_queue_work(&local->hw, &local->work_work);
+               ret = 0;
+               break;
+       }
+       mutex_unlock(&local->mtx);
+
+       return ret;
+}
+
+static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
+                                         struct net_device *dev,
+                                         u16 frame_type, bool reg)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+
+       if (frame_type != (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ))
+               return;
+
+       if (reg)
+               local->probe_req_reg++;
+       else
+               local->probe_req_reg--;
+
+       ieee80211_queue_work(&local->hw, &local->reconfig_filter);
+}
+
+static int ieee80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+
+       if (local->started)
+               return -EOPNOTSUPP;
+
+       return drv_set_antenna(local, tx_ant, rx_ant);
+}
+
+static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+
+       return drv_get_antenna(local, tx_ant, rx_ant);
+}
+
+static int ieee80211_set_ringparam(struct wiphy *wiphy, u32 tx, u32 rx)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+
+       return drv_set_ringparam(local, tx, rx);
+}
+
+static void ieee80211_get_ringparam(struct wiphy *wiphy,
+                                   u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+
+       drv_get_ringparam(local, tx, tx_max, rx, rx_max);
+}
+
 struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
@@ -1623,8 +2109,10 @@ struct cfg80211_ops mac80211_config_ops = {
        .change_mpath = ieee80211_change_mpath,
        .get_mpath = ieee80211_get_mpath,
        .dump_mpath = ieee80211_dump_mpath,
-       .set_mesh_params = ieee80211_set_mesh_params,
-       .get_mesh_params = ieee80211_get_mesh_params,
+       .update_mesh_config = ieee80211_update_mesh_config,
+       .get_mesh_config = ieee80211_get_mesh_config,
+       .join_mesh = ieee80211_join_mesh,
+       .leave_mesh = ieee80211_leave_mesh,
 #endif
        .change_bss = ieee80211_change_bss,
        .set_txq_params = ieee80211_set_txq_params,
@@ -1632,6 +2120,8 @@ struct cfg80211_ops mac80211_config_ops = {
        .suspend = ieee80211_suspend,
        .resume = ieee80211_resume,
        .scan = ieee80211_scan,
+       .sched_scan_start = ieee80211_sched_scan_start,
+       .sched_scan_stop = ieee80211_sched_scan_stop,
        .auth = ieee80211_auth,
        .assoc = ieee80211_assoc,
        .deauth = ieee80211_deauth,
@@ -1648,6 +2138,12 @@ struct cfg80211_ops mac80211_config_ops = {
        .set_bitrate_mask = ieee80211_set_bitrate_mask,
        .remain_on_channel = ieee80211_remain_on_channel,
        .cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
-       .action = ieee80211_action,
+       .mgmt_tx = ieee80211_mgmt_tx,
+       .mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait,
        .set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
+       .mgmt_frame_register = ieee80211_mgmt_frame_register,
+       .set_antenna = ieee80211_set_antenna,
+       .get_antenna = ieee80211_get_antenna,
+       .set_ringparam = ieee80211_set_ringparam,
+       .get_ringparam = ieee80211_get_ringparam,
 };