mac80211: Send Layer 2 Update frame on reassociation
[safe/jmp/linux-2.6] / net / mac80211 / cfg.c
index 5a4c6ed..9d4e4d8 100644 (file)
 #include <net/cfg80211.h>
 #include "ieee80211_i.h"
 #include "cfg.h"
+#include "rate.h"
+#include "mesh.h"
 
-static enum ieee80211_if_types
-nl80211_type_to_mac80211_type(enum nl80211_iftype type)
+static bool nl80211_type_check(enum nl80211_iftype type)
 {
        switch (type) {
-       case NL80211_IFTYPE_UNSPECIFIED:
-               return IEEE80211_IF_TYPE_STA;
        case NL80211_IFTYPE_ADHOC:
-               return IEEE80211_IF_TYPE_IBSS;
        case NL80211_IFTYPE_STATION:
-               return IEEE80211_IF_TYPE_STA;
        case NL80211_IFTYPE_MONITOR:
-               return IEEE80211_IF_TYPE_MNTR;
+#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 IEEE80211_IF_TYPE_INVALID;
+               return false;
        }
 }
 
 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)
+       if (!nl80211_type_check(type))
                return -EINVAL;
 
-       return ieee80211_if_add(local->mdev, name, NULL, itype);
+       err = ieee80211_if_add(local, name, &dev, type, params);
+       if (err || type != NL80211_IFTYPE_MONITOR || !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;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       name = dev->name;
+       ieee80211_if_remove(sdata);
 
-       return ieee80211_if_remove(local->mdev, name, -1);
+       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)
+       if (!nl80211_type_check(type))
                return -EINVAL;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
-               return -EOPNOTSUPP;
+       ret = ieee80211_if_change_type(sdata, type);
+       if (ret)
+               return ret;
+
+       if (netif_running(sdata->dev))
+               return -EBUSY;
+
+       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 != NL80211_IFTYPE_MONITOR || !flags)
+               return 0;
 
+       sdata->u.mntr_flags = *flags;
        return 0;
 }
 
@@ -108,7 +117,8 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_sub_if_data *sdata;
        struct sta_info *sta = NULL;
        enum ieee80211_key_alg alg;
-       int ret;
+       struct ieee80211_key *key;
+       int err;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -127,21 +137,28 @@ 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,
@@ -153,27 +170,37 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
 
        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,
@@ -181,7 +208,7 @@ 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_sub_if_data *sdata;
        struct sta_info *sta = NULL;
        u8 seq[6] = {0};
        struct key_params params;
@@ -190,6 +217,10 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
        u16 iv16;
        int err = -ENOENT;
 
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       rcu_read_lock();
+
        if (mac_addr) {
                sta = sta_info_get(sdata->local, mac_addr);
                if (!sta)
@@ -208,8 +239,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)
@@ -253,8 +284,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;
 }
 
@@ -264,35 +294,106 @@ static int ieee80211_config_default_key(struct wiphy *wiphy,
 {
        struct ieee80211_sub_if_data *sdata;
 
+       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 |
+                       STATION_INFO_TX_BITRATE;
+
+       sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
+       sinfo->rx_bytes = sta->rx_bytes;
+       sinfo->tx_bytes = sta->tx_bytes;
+
+       if (sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
+               sinfo->filled |= STATION_INFO_SIGNAL;
+               sinfo->signal = (s8)sta->last_signal;
+       }
+
+       sinfo->txrate.flags = 0;
+       if (sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)
+               sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
+       if (sta->last_tx_rate.flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+               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;
+
+       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;
+
+       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->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;
 }
 
 /*
@@ -318,8 +419,10 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
         */
        if (params->interval) {
                sdata->local->hw.conf.beacon_int = params->interval;
-               if (ieee80211_hw_config(sdata->local))
-                       return -EINVAL;
+               err = ieee80211_hw_config(sdata->local,
+                                       IEEE80211_CONF_CHANGE_BEACON_INTERVAL);
+               if (err < 0)
+                       return err;
                /*
                 * We updated some parameter so if below bails out
                 * it's not an error.
@@ -390,16 +493,18 @@ 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_sub_if_data *sdata;
        struct beacon_data *old;
 
-       if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (sdata->vif.type != NL80211_IFTYPE_AP)
                return -EINVAL;
 
        old = sdata->u.ap.beacon;
@@ -413,10 +518,12 @@ 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_sub_if_data *sdata;
        struct beacon_data *old;
 
-       if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (sdata->vif.type != NL80211_IFTYPE_AP)
                return -EINVAL;
 
        old = sdata->u.ap.beacon;
@@ -429,10 +536,12 @@ 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_sub_if_data *sdata;
        struct beacon_data *old;
 
-       if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (sdata->vif.type != NL80211_IFTYPE_AP)
                return -EINVAL;
 
        old = sdata->u.ap.beacon;
@@ -444,7 +553,592 @@ 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) */
+struct iapp_layer2_update {
+       u8 da[ETH_ALEN];        /* broadcast */
+       u8 sa[ETH_ALEN];        /* STA addr */
+       __be16 len;             /* 6 */
+       u8 dsap;                /* 0 */
+       u8 ssap;                /* 0 */
+       u8 control;
+       u8 xid_info[3];
+} __attribute__ ((packed));
+
+static void ieee80211_send_layer2_update(struct sta_info *sta)
+{
+       struct iapp_layer2_update *msg;
+       struct sk_buff *skb;
+
+       /* Send Level 2 Update Frame to update forwarding tables in layer 2
+        * bridge devices */
+
+       skb = dev_alloc_skb(sizeof(*msg));
+       if (!skb)
+               return;
+       msg = (struct iapp_layer2_update *)skb_put(skb, sizeof(*msg));
+
+       /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID)
+        * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */
+
+       memset(msg->da, 0xff, ETH_ALEN);
+       memcpy(msg->sa, sta->sta.addr, ETH_ALEN);
+       msg->len = htons(6);
+       msg->dsap = 0;
+       msg->ssap = 0x01;       /* NULL LSAP, CR Bit: Response */
+       msg->control = 0xaf;    /* XID response lsb.1111F101.
+                                * F=0 (no poll command; unsolicited frame) */
+       msg->xid_info[0] = 0x81;        /* XID format identifier */
+       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->sdata->dev;
+       skb->protocol = eth_type_trans(skb, sta->sdata->dev);
+       memset(skb->cb, 0, sizeof(skb->cb));
+       netif_rx(skb);
+}
+
+static void sta_apply_parameters(struct ieee80211_local *local,
+                                struct sta_info *sta,
+                                struct station_parameters *params)
+{
+       u32 rates;
+       int i, j;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+       sband = local->hw.wiphy->bands[local->oper_channel->band];
+
+       /*
+        * 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;
+
+               sta->flags &= ~WLAN_STA_SHORT_PREAMBLE;
+               if (params->station_flags & STATION_FLAG_SHORT_PREAMBLE)
+                       sta->flags |= WLAN_STA_SHORT_PREAMBLE;
+
+               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->sta.aid = params->aid;
+               if (sta->sta.aid > IEEE80211_MAX_AID)
+                       sta->sta.aid = 0; /* XXX: should this be an error? */
+       }
+
+       if (params->listen_interval >= 0)
+               sta->listen_interval = params->listen_interval;
+
+       if (params->supported_rates) {
+               rates = 0;
+
+               for (i = 0; i < params->supported_rates_len; i++) {
+                       int rate = (params->supported_rates[i] & 0x7f) * 5;
+                       for (j = 0; j < sband->n_bitrates; j++) {
+                               if (sband->bitrates[j].bitrate == rate)
+                                       rates |= BIT(j);
+                       }
+               }
+               sta->sta.supp_rates[local->oper_channel->band] = rates;
+       }
+
+       if (params->ht_capa)
+               ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
+                                                 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;
+               }
+       }
+}
+
+static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
+                                u8 *mac, struct station_parameters *params)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct sta_info *sta;
+       struct ieee80211_sub_if_data *sdata;
+       int err;
+       int layer2_update;
+
+       /* Prevent a race with changing the rate control algorithm */
+       if (!netif_running(dev))
+               return -ENETDOWN;
+
+       if (params->vlan) {
+               sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
+
+               if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+                   sdata->vif.type != NL80211_IFTYPE_AP)
+                       return -EINVAL;
+       } else
+               sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       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->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
+
+       sta_apply_parameters(local, sta, params);
+
+       rate_control_rate_init(sta);
+
+       layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+               sdata->vif.type == NL80211_IFTYPE_AP;
+
+       rcu_read_lock();
+
+       err = sta_info_insert(sta);
+       if (err) {
+               /* STA has been freed */
+               if (err == -EEXIST && layer2_update) {
+                       /* Need to update layer 2 devices on reassociation */
+                       sta = sta_info_get(local, mac);
+                       if (sta)
+                               ieee80211_send_layer2_update(sta);
+               }
+               rcu_read_unlock();
+               return err;
+       }
+
+       if (layer2_update)
+               ieee80211_send_layer2_update(sta);
+
+       rcu_read_unlock();
+
+       return 0;
+}
+
+static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
+                                u8 *mac)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata;
+       struct sta_info *sta;
+
+       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) {
+                       rcu_read_unlock();
+                       return -ENOENT;
+               }
+
+               sta_info_unlink(&sta);
+               rcu_read_unlock();
+
+               sta_info_destroy(sta);
+       } else
+               sta_info_flush(local, sdata);
+
+       return 0;
+}
+
+static int ieee80211_change_station(struct wiphy *wiphy,
+                                   struct net_device *dev,
+                                   u8 *mac,
+                                   struct station_parameters *params)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct sta_info *sta;
+       struct ieee80211_sub_if_data *vlansdata;
+
+       rcu_read_lock();
+
+       /* XXX: get sta belonging to dev */
+       sta = sta_info_get(local, mac);
+       if (!sta) {
+               rcu_read_unlock();
+               return -ENOENT;
+       }
+
+       if (params->vlan && params->vlan != sta->sdata->dev) {
+               vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
+
+               if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+                   vlansdata->vif.type != NL80211_IFTYPE_AP) {
+                       rcu_read_unlock();
+                       return -EINVAL;
+               }
+
+               sta->sdata = vlansdata;
+               ieee80211_send_layer2_update(sta);
+       }
+
+       sta_apply_parameters(local, sta, params);
+
+       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 (!netif_running(dev))
+               return -ENETDOWN;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (sdata->vif.type != NL80211_IFTYPE_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 (!netif_running(dev))
+               return -ENETDOWN;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (sdata->vif.type != NL80211_IFTYPE_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->sta.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_sub_if_data *sdata;
+       struct mesh_path *mpath;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (sdata->vif.type != NL80211_IFTYPE_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_sub_if_data *sdata;
+       struct mesh_path *mpath;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (sdata->vif.type != NL80211_IFTYPE_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;
+}
+
+static int ieee80211_get_mesh_params(struct wiphy *wiphy,
+                               struct net_device *dev,
+                               struct mesh_config *conf)
+{
+       struct ieee80211_sub_if_data *sdata;
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
+               return -ENOTSUPP;
+       memcpy(conf, &(sdata->u.mesh.mshcfg), sizeof(struct mesh_config));
+       return 0;
+}
+
+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)
+{
+       struct mesh_config *conf;
+       struct ieee80211_sub_if_data *sdata;
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
+               return -ENOTSUPP;
+
+       /* Set the config options which we are interested in setting */
+       conf = &(sdata->u.mesh.mshcfg);
+       if (_chg_mesh_attr(NL80211_MESHCONF_RETRY_TIMEOUT, mask))
+               conf->dot11MeshRetryTimeout = nconf->dot11MeshRetryTimeout;
+       if (_chg_mesh_attr(NL80211_MESHCONF_CONFIRM_TIMEOUT, mask))
+               conf->dot11MeshConfirmTimeout = nconf->dot11MeshConfirmTimeout;
+       if (_chg_mesh_attr(NL80211_MESHCONF_HOLDING_TIMEOUT, mask))
+               conf->dot11MeshHoldingTimeout = nconf->dot11MeshHoldingTimeout;
+       if (_chg_mesh_attr(NL80211_MESHCONF_MAX_PEER_LINKS, mask))
+               conf->dot11MeshMaxPeerLinks = nconf->dot11MeshMaxPeerLinks;
+       if (_chg_mesh_attr(NL80211_MESHCONF_MAX_RETRIES, mask))
+               conf->dot11MeshMaxRetries = nconf->dot11MeshMaxRetries;
+       if (_chg_mesh_attr(NL80211_MESHCONF_TTL, mask))
+               conf->dot11MeshTTL = nconf->dot11MeshTTL;
+       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))
+               conf->dot11MeshHWMPmaxPREQretries =
+                       nconf->dot11MeshHWMPmaxPREQretries;
+       if (_chg_mesh_attr(NL80211_MESHCONF_PATH_REFRESH_TIME, mask))
+               conf->path_refresh_time = nconf->path_refresh_time;
+       if (_chg_mesh_attr(NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, mask))
+               conf->min_discovery_timeout = nconf->min_discovery_timeout;
+       if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, mask))
+               conf->dot11MeshHWMPactivePathTimeout =
+                       nconf->dot11MeshHWMPactivePathTimeout;
+       if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, mask))
+               conf->dot11MeshHWMPpreqMinInterval =
+                       nconf->dot11MeshHWMPpreqMinInterval;
+       if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+                          mask))
+               conf->dot11MeshHWMPnetDiameterTraversalTime =
+                       nconf->dot11MeshHWMPnetDiameterTraversalTime;
+       return 0;
+}
+
+#endif
+
+static int ieee80211_change_bss(struct wiphy *wiphy,
+                               struct net_device *dev,
+                               struct bss_parameters *params)
+{
+       struct ieee80211_sub_if_data *sdata;
+       u32 changed = 0;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (sdata->vif.type != NL80211_IFTYPE_AP)
+               return -EINVAL;
+
+       if (params->use_cts_prot >= 0) {
+               sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot;
+               changed |= BSS_CHANGED_ERP_CTS_PROT;
+       }
+       if (params->use_short_preamble >= 0) {
+               sdata->vif.bss_conf.use_short_preamble =
+                       params->use_short_preamble;
+               changed |= BSS_CHANGED_ERP_PREAMBLE;
+       }
+       if (params->use_short_slot_time >= 0) {
+               sdata->vif.bss_conf.use_short_slot =
+                       params->use_short_slot_time;
+               changed |= BSS_CHANGED_ERP_SLOT;
+       }
+
+       if (params->basic_rates) {
+               int i, j;
+               u32 rates = 0;
+               struct ieee80211_local *local = wiphy_priv(wiphy);
+               struct ieee80211_supported_band *sband =
+                       wiphy->bands[local->oper_channel->band];
+
+               for (i = 0; i < params->basic_rates_len; i++) {
+                       int rate = (params->basic_rates[i] & 0x7f) * 5;
+                       for (j = 0; j < sband->n_bitrates; j++) {
+                               if (sband->bitrates[j].bitrate == rate)
+                                       rates |= BIT(j);
+                       }
+               }
+               sdata->vif.bss_conf.basic_rates = rates;
+               changed |= BSS_CHANGED_BASIC_RATES;
+       }
+
+       ieee80211_bss_info_change_notify(sdata, changed);
+
+       return 0;
+}
+
+static int ieee80211_set_txq_params(struct wiphy *wiphy,
+                                   struct ieee80211_txq_params *params)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_tx_queue_params p;
+
+       if (!local->ops->conf_tx)
+               return -EOPNOTSUPP;
+
+       memset(&p, 0, sizeof(p));
+       p.aifs = params->aifs;
+       p.cw_max = params->cwmax;
+       p.cw_min = params->cwmin;
+       p.txop = params->txop;
+       if (local->ops->conf_tx(local_to_hw(local), params->queue, &p)) {
+               printk(KERN_DEBUG "%s: failed to set TX queue "
+                      "parameters for queue %d\n", local->mdev->name,
+                      params->queue);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ieee80211_set_channel(struct wiphy *wiphy,
+                                struct ieee80211_channel *chan,
+                                enum nl80211_channel_type channel_type)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+
+       local->oper_channel = chan;
+       local->oper_channel_type = channel_type;
+
+       return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
 }
 
 struct cfg80211_ops mac80211_config_ops = {
@@ -458,5 +1152,21 @@ struct cfg80211_ops mac80211_config_ops = {
        .add_beacon = ieee80211_add_beacon,
        .set_beacon = ieee80211_set_beacon,
        .del_beacon = ieee80211_del_beacon,
+       .add_station = ieee80211_add_station,
+       .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,
+       .set_mesh_params = ieee80211_set_mesh_params,
+       .get_mesh_params = ieee80211_get_mesh_params,
+#endif
+       .change_bss = ieee80211_change_bss,
+       .set_txq_params = ieee80211_set_txq_params,
+       .set_channel = ieee80211_set_channel,
 };