mac80211: split off mesh handling entirely
[safe/jmp/linux-2.6] / net / mac80211 / cfg.c
index 22c9619..6ec2127 100644 (file)
 #include <net/cfg80211.h>
 #include "ieee80211_i.h"
 #include "cfg.h"
-#include "ieee80211_rate.h"
+#include "rate.h"
+#include "mesh.h"
+
+struct ieee80211_hw *wiphy_to_hw(struct wiphy *wiphy)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       return &local->hw;
+}
+EXPORT_SYMBOL(wiphy_to_hw);
 
 static enum ieee80211_if_types
 nl80211_type_to_mac80211_type(enum nl80211_iftype type)
@@ -28,77 +36,94 @@ nl80211_type_to_mac80211_type(enum nl80211_iftype type)
                return IEEE80211_IF_TYPE_STA;
        case NL80211_IFTYPE_MONITOR:
                return IEEE80211_IF_TYPE_MNTR;
+#ifdef CONFIG_MAC80211_MESH
+       case NL80211_IFTYPE_MESH_POINT:
+               return IEEE80211_IF_TYPE_MESH_POINT;
+#endif
+       case NL80211_IFTYPE_WDS:
+               return IEEE80211_IF_TYPE_WDS;
        default:
                return IEEE80211_IF_TYPE_INVALID;
        }
 }
 
 static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
-                              enum nl80211_iftype type)
+                              enum nl80211_iftype type, u32 *flags,
+                              struct vif_params *params)
 {
        struct ieee80211_local *local = wiphy_priv(wiphy);
        enum ieee80211_if_types itype;
-
-       if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED))
-               return -ENODEV;
+       struct net_device *dev;
+       struct ieee80211_sub_if_data *sdata;
+       int err;
 
        itype = nl80211_type_to_mac80211_type(type);
        if (itype == IEEE80211_IF_TYPE_INVALID)
                return -EINVAL;
 
-       return ieee80211_if_add(local->mdev, name, NULL, itype);
+       err = ieee80211_if_add(local, name, &dev, itype, params);
+       if (err || itype != IEEE80211_IF_TYPE_MNTR || !flags)
+               return err;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       sdata->u.mntr_flags = *flags;
+       return 0;
 }
 
 static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex)
 {
-       struct ieee80211_local *local = wiphy_priv(wiphy);
        struct net_device *dev;
-       char *name;
-
-       if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED))
-               return -ENODEV;
+       struct ieee80211_sub_if_data *sdata;
 
        /* we're under RTNL */
        dev = __dev_get_by_index(&init_net, ifindex);
        if (!dev)
-               return 0;
+               return -ENODEV;
 
-       name = dev->name;
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       return ieee80211_if_remove(local->mdev, name, -1);
+       ieee80211_if_remove(sdata);
+
+       return 0;
 }
 
 static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex,
-                                 enum nl80211_iftype type)
+                                 enum nl80211_iftype type, u32 *flags,
+                                 struct vif_params *params)
 {
        struct ieee80211_local *local = wiphy_priv(wiphy);
        struct net_device *dev;
        enum ieee80211_if_types itype;
        struct ieee80211_sub_if_data *sdata;
-
-       if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED))
-               return -ENODEV;
+       int ret;
 
        /* we're under RTNL */
        dev = __dev_get_by_index(&init_net, ifindex);
        if (!dev)
                return -ENODEV;
 
-       if (netif_running(dev))
-               return -EBUSY;
-
        itype = nl80211_type_to_mac80211_type(type);
        if (itype == IEEE80211_IF_TYPE_INVALID)
                return -EINVAL;
 
+       if (dev == local->mdev)
+               return -EOPNOTSUPP;
+
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
-               return -EOPNOTSUPP;
+       ret = ieee80211_if_change_type(sdata, itype);
+       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);
 
-       ieee80211_if_reinit(dev);
-       ieee80211_if_set_type(dev, itype);
+       if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR || !flags)
+               return 0;
 
+       sdata->u.mntr_flags = *flags;
        return 0;
 }
 
@@ -106,10 +131,15 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
                             u8 key_idx, u8 *mac_addr,
                             struct key_params *params)
 {
+       struct ieee80211_local *local = wiphy_priv(wiphy);
        struct ieee80211_sub_if_data *sdata;
        struct sta_info *sta = NULL;
        enum ieee80211_key_alg alg;
-       int ret;
+       struct ieee80211_key *key;
+       int err;
+
+       if (dev == local->mdev)
+               return -EOPNOTSUPP;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -128,53 +158,74 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
                return -EINVAL;
        }
 
+       key = ieee80211_key_alloc(alg, key_idx, params->key_len, params->key);
+       if (!key)
+               return -ENOMEM;
+
+       rcu_read_lock();
+
        if (mac_addr) {
                sta = sta_info_get(sdata->local, mac_addr);
-               if (!sta)
-                       return -ENOENT;
+               if (!sta) {
+                       ieee80211_key_free(key);
+                       err = -ENOENT;
+                       goto out_unlock;
+               }
        }
 
-       ret = 0;
-       if (!ieee80211_key_alloc(sdata, sta, alg, key_idx,
-                                params->key_len, params->key))
-               ret = -ENOMEM;
+       ieee80211_key_link(key, sdata, sta);
 
-       if (sta)
-               sta_info_put(sta);
+       err = 0;
+ out_unlock:
+       rcu_read_unlock();
 
-       return ret;
+       return err;
 }
 
 static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
                             u8 key_idx, u8 *mac_addr)
 {
+       struct ieee80211_local *local = wiphy_priv(wiphy);
        struct ieee80211_sub_if_data *sdata;
        struct sta_info *sta;
        int ret;
 
+       if (dev == local->mdev)
+               return -EOPNOTSUPP;
+
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
+       rcu_read_lock();
+
        if (mac_addr) {
+               ret = -ENOENT;
+
                sta = sta_info_get(sdata->local, mac_addr);
                if (!sta)
-                       return -ENOENT;
+                       goto out_unlock;
 
-               ret = 0;
-               if (sta->key)
+               if (sta->key) {
                        ieee80211_key_free(sta->key);
-               else
-                       ret = -ENOENT;
+                       WARN_ON(sta->key);
+                       ret = 0;
+               }
 
-               sta_info_put(sta);
-               return ret;
+               goto out_unlock;
        }
 
-       if (!sdata->keys[key_idx])
-               return -ENOENT;
+       if (!sdata->keys[key_idx]) {
+               ret = -ENOENT;
+               goto out_unlock;
+       }
 
        ieee80211_key_free(sdata->keys[key_idx]);
+       WARN_ON(sdata->keys[key_idx]);
 
-       return 0;
+       ret = 0;
+ out_unlock:
+       rcu_read_unlock();
+
+       return ret;
 }
 
 static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
@@ -182,7 +233,8 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
                             void (*callback)(void *cookie,
                                              struct key_params *params))
 {
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata;
        struct sta_info *sta = NULL;
        u8 seq[6] = {0};
        struct key_params params;
@@ -191,6 +243,13 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
        u16 iv16;
        int err = -ENOENT;
 
+       if (dev == local->mdev)
+               return -EOPNOTSUPP;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       rcu_read_lock();
+
        if (mac_addr) {
                sta = sta_info_get(sdata->local, mac_addr);
                if (!sta)
@@ -209,8 +268,8 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
        case ALG_TKIP:
                params.cipher = WLAN_CIPHER_SUITE_TKIP;
 
-               iv32 = key->u.tkip.iv32;
-               iv16 = key->u.tkip.iv16;
+               iv32 = key->u.tkip.tx.iv32;
+               iv16 = key->u.tkip.tx.iv16;
 
                if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
                    sdata->local->ops->get_tkip_seq)
@@ -254,8 +313,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
        err = 0;
 
  out:
-       if (sta)
-               sta_info_put(sta);
+       rcu_read_unlock();
        return err;
 }
 
@@ -263,37 +321,89 @@ static int ieee80211_config_default_key(struct wiphy *wiphy,
                                        struct net_device *dev,
                                        u8 key_idx)
 {
+       struct ieee80211_local *local = wiphy_priv(wiphy);
        struct ieee80211_sub_if_data *sdata;
 
+       if (dev == local->mdev)
+               return -EOPNOTSUPP;
+
+       rcu_read_lock();
+
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        ieee80211_set_default_key(sdata, key_idx);
 
+       rcu_read_unlock();
+
        return 0;
 }
 
+static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
+{
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+       sinfo->filled = STATION_INFO_INACTIVE_TIME |
+                       STATION_INFO_RX_BYTES |
+                       STATION_INFO_TX_BYTES;
+
+       sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
+       sinfo->rx_bytes = sta->rx_bytes;
+       sinfo->tx_bytes = sta->tx_bytes;
+
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+#ifdef CONFIG_MAC80211_MESH
+               sinfo->filled |= STATION_INFO_LLID |
+                                STATION_INFO_PLID |
+                                STATION_INFO_PLINK_STATE;
+
+               sinfo->llid = le16_to_cpu(sta->llid);
+               sinfo->plid = le16_to_cpu(sta->plid);
+               sinfo->plink_state = sta->plink_state;
+#endif
+       }
+}
+
+
+static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
+                                int idx, u8 *mac, struct station_info *sinfo)
+{
+       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+       struct sta_info *sta;
+       int ret = -ENOENT;
+
+       rcu_read_lock();
+
+       sta = sta_info_get_by_idx(local, idx, dev);
+       if (sta) {
+               ret = 0;
+               memcpy(mac, sta->addr, ETH_ALEN);
+               sta_set_sinfo(sta, sinfo);
+       }
+
+       rcu_read_unlock();
+
+       return ret;
+}
+
 static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
-                                u8 *mac, struct station_stats *stats)
+                                u8 *mac, struct station_info *sinfo)
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct sta_info *sta;
+       int ret = -ENOENT;
 
-       sta = sta_info_get(local, mac);
-       if (!sta)
-               return -ENOENT;
+       rcu_read_lock();
 
        /* XXX: verify sta->dev == dev */
 
-       stats->filled = STATION_STAT_INACTIVE_TIME |
-                       STATION_STAT_RX_BYTES |
-                       STATION_STAT_TX_BYTES;
-
-       stats->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
-       stats->rx_bytes = sta->rx_bytes;
-       stats->tx_bytes = sta->tx_bytes;
+       sta = sta_info_get(local, mac);
+       if (sta) {
+               ret = 0;
+               sta_set_sinfo(sta, sinfo);
+       }
 
-       sta_info_put(sta);
+       rcu_read_unlock();
 
-       return 0;
+       return ret;
 }
 
 /*
@@ -391,15 +501,21 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
 
        kfree(old);
 
-       return ieee80211_if_config_beacon(sdata->dev);
+       return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
 }
 
 static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,
                                struct beacon_parameters *params)
 {
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata;
        struct beacon_data *old;
 
+       if (dev == local->mdev)
+               return -EOPNOTSUPP;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
        if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
                return -EINVAL;
 
@@ -414,9 +530,15 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,
 static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev,
                                struct beacon_parameters *params)
 {
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata;
        struct beacon_data *old;
 
+       if (dev == local->mdev)
+               return -EOPNOTSUPP;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
        if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
                return -EINVAL;
 
@@ -430,9 +552,15 @@ static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev,
 
 static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev)
 {
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata;
        struct beacon_data *old;
 
+       if (dev == local->mdev)
+               return -EOPNOTSUPP;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
        if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
                return -EINVAL;
 
@@ -445,7 +573,7 @@ static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev)
        synchronize_rcu();
        kfree(old);
 
-       return ieee80211_if_config_beacon(dev);
+       return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
 }
 
 /* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */
@@ -486,8 +614,8 @@ static void ieee80211_send_layer2_update(struct sta_info *sta)
        msg->xid_info[1] = 1;   /* LLC types/classes: Type 1 LLC */
        msg->xid_info[2] = 0;   /* XID sender's receive window size (RW) */
 
-       skb->dev = sta->dev;
-       skb->protocol = eth_type_trans(skb, sta->dev);
+       skb->dev = sta->sdata->dev;
+       skb->protocol = eth_type_trans(skb, sta->sdata->dev);
        memset(skb->cb, 0, sizeof(skb->cb));
        netif_rx(skb);
 }
@@ -498,9 +626,17 @@ static void sta_apply_parameters(struct ieee80211_local *local,
 {
        u32 rates;
        int i, j;
-       struct ieee80211_hw_mode *mode;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+       /*
+        * FIXME: updating the flags is racy when this function is
+        *        called from ieee80211_change_station(), this will
+        *        be resolved in a future patch.
+        */
 
        if (params->station_flags & STATION_FLAG_CHANGED) {
+               spin_lock_bh(&sta->lock);
                sta->flags &= ~WLAN_STA_AUTHORIZED;
                if (params->station_flags & STATION_FLAG_AUTHORIZED)
                        sta->flags |= WLAN_STA_AUTHORIZED;
@@ -512,8 +648,16 @@ static void sta_apply_parameters(struct ieee80211_local *local,
                sta->flags &= ~WLAN_STA_WME;
                if (params->station_flags & STATION_FLAG_WME)
                        sta->flags |= WLAN_STA_WME;
+               spin_unlock_bh(&sta->lock);
        }
 
+       /*
+        * FIXME: updating the following information is racy when this
+        *        function is called from ieee80211_change_station().
+        *        However, all this information should be static so
+        *        maybe we should just reject attemps to change it.
+        */
+
        if (params->aid) {
                sta->aid = params->aid;
                if (sta->aid > IEEE80211_MAX_AID)
@@ -525,61 +669,89 @@ static void sta_apply_parameters(struct ieee80211_local *local,
 
        if (params->supported_rates) {
                rates = 0;
-               mode = local->oper_hw_mode;
+               sband = local->hw.wiphy->bands[local->oper_channel->band];
+
                for (i = 0; i < params->supported_rates_len; i++) {
                        int rate = (params->supported_rates[i] & 0x7f) * 5;
-                       for (j = 0; j < mode->num_rates; j++) {
-                               if (mode->rates[j].rate == rate)
+                       for (j = 0; j < sband->n_bitrates; j++) {
+                               if (sband->bitrates[j].bitrate == rate)
                                        rates |= BIT(j);
                        }
                }
-               sta->supp_rates = rates;
+               sta->supp_rates[local->oper_channel->band] = rates;
+       }
+
+       if (params->ht_capa) {
+               ieee80211_ht_cap_ie_to_ht_info(params->ht_capa,
+                                              &sta->ht_info);
+       }
+
+       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;
+               }
        }
 }
 
 static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
                                 u8 *mac, struct station_parameters *params)
 {
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+       struct ieee80211_local *local = wiphy_priv(wiphy);
        struct sta_info *sta;
        struct ieee80211_sub_if_data *sdata;
+       int err;
+
+       if (dev == local->mdev || params->vlan == local->mdev)
+               return -EOPNOTSUPP;
 
        /* Prevent a race with changing the rate control algorithm */
        if (!netif_running(dev))
                return -ENETDOWN;
 
-       /* XXX: get sta belonging to dev */
-       sta = sta_info_get(local, mac);
-       if (sta) {
-               sta_info_put(sta);
-               return -EEXIST;
-       }
-
        if (params->vlan) {
                sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
 
-               if (sdata->vif.type != IEEE80211_IF_TYPE_VLAN ||
+               if (sdata->vif.type != IEEE80211_IF_TYPE_VLAN &&
                    sdata->vif.type != IEEE80211_IF_TYPE_AP)
                        return -EINVAL;
        } else
                sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       sta = sta_info_add(local, dev, mac, GFP_KERNEL);
+       if (compare_ether_addr(mac, dev->dev_addr) == 0)
+               return -EINVAL;
+
+       if (is_multicast_ether_addr(mac))
+               return -EINVAL;
+
+       sta = sta_info_alloc(sdata, mac, GFP_KERNEL);
        if (!sta)
                return -ENOMEM;
 
-       sta->dev = sdata->dev;
-       if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN ||
-           sdata->vif.type == IEEE80211_IF_TYPE_AP)
-               ieee80211_send_layer2_update(sta);
-
        sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
 
        sta_apply_parameters(local, sta, params);
 
        rate_control_rate_init(sta, local);
 
-       sta_info_put(sta);
+       rcu_read_lock();
+
+       err = sta_info_insert(sta);
+       if (err) {
+               /* STA has been freed */
+               rcu_read_unlock();
+               return err;
+       }
+
+       if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN ||
+           sdata->vif.type == IEEE80211_IF_TYPE_AP)
+               ieee80211_send_layer2_update(sta);
+
+       rcu_read_unlock();
 
        return 0;
 }
@@ -587,19 +759,31 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
                                 u8 *mac)
 {
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata;
        struct sta_info *sta;
 
+       if (dev == local->mdev)
+               return -EOPNOTSUPP;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
        if (mac) {
+               rcu_read_lock();
+
                /* XXX: get sta belonging to dev */
                sta = sta_info_get(local, mac);
-               if (!sta)
+               if (!sta) {
+                       rcu_read_unlock();
                        return -ENOENT;
+               }
 
-               sta_info_free(sta);
-               sta_info_put(sta);
+               sta_info_unlink(&sta);
+               rcu_read_unlock();
+
+               sta_info_destroy(sta);
        } else
-               sta_info_flush(local, dev);
+               sta_info_flush(local, sdata);
 
        return 0;
 }
@@ -609,29 +793,267 @@ static int ieee80211_change_station(struct wiphy *wiphy,
                                    u8 *mac,
                                    struct station_parameters *params)
 {
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+       struct ieee80211_local *local = wiphy_priv(wiphy);
        struct sta_info *sta;
        struct ieee80211_sub_if_data *vlansdata;
 
+       if (dev == local->mdev || params->vlan == local->mdev)
+               return -EOPNOTSUPP;
+
+       rcu_read_lock();
+
        /* XXX: get sta belonging to dev */
        sta = sta_info_get(local, mac);
-       if (!sta)
+       if (!sta) {
+               rcu_read_unlock();
                return -ENOENT;
+       }
 
-       if (params->vlan && params->vlan != sta->dev) {
+       if (params->vlan && params->vlan != sta->sdata->dev) {
                vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
 
-               if (vlansdata->vif.type != IEEE80211_IF_TYPE_VLAN ||
-                   vlansdata->vif.type != IEEE80211_IF_TYPE_AP)
+               if (vlansdata->vif.type != IEEE80211_IF_TYPE_VLAN &&
+                   vlansdata->vif.type != IEEE80211_IF_TYPE_AP) {
+                       rcu_read_unlock();
                        return -EINVAL;
+               }
 
-               sta->dev = params->vlan;
+               sta->sdata = vlansdata;
                ieee80211_send_layer2_update(sta);
        }
 
        sta_apply_parameters(local, sta, params);
 
-       sta_info_put(sta);
+       rcu_read_unlock();
+
+       return 0;
+}
+
+#ifdef CONFIG_MAC80211_MESH
+static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
+                                u8 *dst, u8 *next_hop)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata;
+       struct mesh_path *mpath;
+       struct sta_info *sta;
+       int err;
+
+       if (dev == local->mdev)
+               return -EOPNOTSUPP;
+
+       if (!netif_running(dev))
+               return -ENETDOWN;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
+               return -ENOTSUPP;
+
+       rcu_read_lock();
+       sta = sta_info_get(local, next_hop);
+       if (!sta) {
+               rcu_read_unlock();
+               return -ENOENT;
+       }
+
+       err = mesh_path_add(dst, sdata);
+       if (err) {
+               rcu_read_unlock();
+               return err;
+       }
+
+       mpath = mesh_path_lookup(dst, sdata);
+       if (!mpath) {
+               rcu_read_unlock();
+               return -ENXIO;
+       }
+       mesh_path_fix_nexthop(mpath, sta);
+
+       rcu_read_unlock();
+       return 0;
+}
+
+static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
+                                u8 *dst)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (dst)
+               return mesh_path_del(dst, sdata);
+
+       mesh_path_flush(sdata);
+       return 0;
+}
+
+static int ieee80211_change_mpath(struct wiphy *wiphy,
+                                   struct net_device *dev,
+                                   u8 *dst, u8 *next_hop)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata;
+       struct mesh_path *mpath;
+       struct sta_info *sta;
+
+       if (dev == local->mdev)
+               return -EOPNOTSUPP;
+
+       if (!netif_running(dev))
+               return -ENETDOWN;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
+               return -ENOTSUPP;
+
+       rcu_read_lock();
+
+       sta = sta_info_get(local, next_hop);
+       if (!sta) {
+               rcu_read_unlock();
+               return -ENOENT;
+       }
+
+       mpath = mesh_path_lookup(dst, sdata);
+       if (!mpath) {
+               rcu_read_unlock();
+               return -ENOENT;
+       }
+
+       mesh_path_fix_nexthop(mpath, sta);
+
+       rcu_read_unlock();
+       return 0;
+}
+
+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->addr, ETH_ALEN);
+       else
+               memset(next_hop, 0, ETH_ALEN);
+
+       pinfo->filled = MPATH_INFO_FRAME_QLEN |
+                       MPATH_INFO_DSN |
+                       MPATH_INFO_METRIC |
+                       MPATH_INFO_EXPTIME |
+                       MPATH_INFO_DISCOVERY_TIMEOUT |
+                       MPATH_INFO_DISCOVERY_RETRIES |
+                       MPATH_INFO_FLAGS;
+
+       pinfo->frame_qlen = mpath->frame_queue.qlen;
+       pinfo->dsn = mpath->dsn;
+       pinfo->metric = mpath->metric;
+       if (time_before(jiffies, mpath->exp_time))
+               pinfo->exptime = jiffies_to_msecs(mpath->exp_time - jiffies);
+       pinfo->discovery_timeout =
+                       jiffies_to_msecs(mpath->discovery_timeout);
+       pinfo->discovery_retries = mpath->discovery_retries;
+       pinfo->flags = 0;
+       if (mpath->flags & MESH_PATH_ACTIVE)
+               pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE;
+       if (mpath->flags & MESH_PATH_RESOLVING)
+               pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
+       if (mpath->flags & MESH_PATH_DSN_VALID)
+               pinfo->flags |= NL80211_MPATH_FLAG_DSN_VALID;
+       if (mpath->flags & MESH_PATH_FIXED)
+               pinfo->flags |= NL80211_MPATH_FLAG_FIXED;
+       if (mpath->flags & MESH_PATH_RESOLVING)
+               pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
+
+       pinfo->flags = mpath->flags;
+}
+
+static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
+                              u8 *dst, u8 *next_hop, struct mpath_info *pinfo)
+
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata;
+       struct mesh_path *mpath;
+
+       if (dev == local->mdev)
+               return -EOPNOTSUPP;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
+               return -ENOTSUPP;
+
+       rcu_read_lock();
+       mpath = mesh_path_lookup(dst, sdata);
+       if (!mpath) {
+               rcu_read_unlock();
+               return -ENOENT;
+       }
+       memcpy(dst, mpath->dst, ETH_ALEN);
+       mpath_set_pinfo(mpath, next_hop, pinfo);
+       rcu_read_unlock();
+       return 0;
+}
+
+static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
+                                int idx, u8 *dst, u8 *next_hop,
+                                struct mpath_info *pinfo)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata;
+       struct mesh_path *mpath;
+
+       if (dev == local->mdev)
+               return -EOPNOTSUPP;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
+               return -ENOTSUPP;
+
+       rcu_read_lock();
+       mpath = mesh_path_lookup_by_idx(idx, sdata);
+       if (!mpath) {
+               rcu_read_unlock();
+               return -ENOENT;
+       }
+       memcpy(dst, mpath->dst, ETH_ALEN);
+       mpath_set_pinfo(mpath, next_hop, pinfo);
+       rcu_read_unlock();
+       return 0;
+}
+#endif
+
+static int ieee80211_change_bss(struct wiphy *wiphy,
+                               struct net_device *dev,
+                               struct bss_parameters *params)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata;
+       u32 changed = 0;
+
+       if (dev == local->mdev)
+               return -EOPNOTSUPP;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
+               return -EINVAL;
+
+       if (params->use_cts_prot >= 0) {
+               sdata->bss_conf.use_cts_prot = params->use_cts_prot;
+               changed |= BSS_CHANGED_ERP_CTS_PROT;
+       }
+       if (params->use_short_preamble >= 0) {
+               sdata->bss_conf.use_short_preamble =
+                       params->use_short_preamble;
+               changed |= BSS_CHANGED_ERP_PREAMBLE;
+       }
+       if (params->use_short_slot_time >= 0) {
+               sdata->bss_conf.use_short_slot =
+                       params->use_short_slot_time;
+               changed |= BSS_CHANGED_ERP_SLOT;
+       }
+
+       ieee80211_bss_info_change_notify(sdata, changed);
 
        return 0;
 }
@@ -651,4 +1073,13 @@ struct cfg80211_ops mac80211_config_ops = {
        .del_station = ieee80211_del_station,
        .change_station = ieee80211_change_station,
        .get_station = ieee80211_get_station,
+       .dump_station = ieee80211_dump_station,
+#ifdef CONFIG_MAC80211_MESH
+       .add_mpath = ieee80211_add_mpath,
+       .del_mpath = ieee80211_del_mpath,
+       .change_mpath = ieee80211_change_mpath,
+       .get_mpath = ieee80211_get_mpath,
+       .dump_mpath = ieee80211_dump_mpath,
+#endif
+       .change_bss = ieee80211_change_bss,
 };