mac80211: Send Layer 2 Update frame on reassociation
[safe/jmp/linux-2.6] / net / mac80211 / cfg.c
index 8ea3090..9d4e4d8 100644 (file)
@@ -26,6 +26,8 @@ static bool nl80211_type_check(enum nl80211_iftype type)
 #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:
@@ -308,12 +310,35 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
 
        sinfo->filled = STATION_INFO_INACTIVE_TIME |
                        STATION_INFO_RX_BYTES |
-                       STATION_INFO_TX_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 |
@@ -394,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;
-               ieee80211_hw_config(sdata->local,
-                                   IEEE80211_CONF_CHANGE_BEACON_INTERVAL);
+               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.
@@ -582,6 +609,8 @@ static void sta_apply_parameters(struct ieee80211_local *local,
        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
@@ -622,7 +651,6 @@ static void sta_apply_parameters(struct ieee80211_local *local,
 
        if (params->supported_rates) {
                rates = 0;
-               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;
@@ -635,7 +663,8 @@ static void sta_apply_parameters(struct ieee80211_local *local,
        }
 
        if (params->ht_capa)
-               ieee80211_ht_cap_ie_to_sta_ht_cap(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) {
@@ -657,6 +686,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        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))
@@ -687,17 +717,25 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 
        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 (sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
-           sdata->vif.type == NL80211_IFTYPE_AP)
+       if (layer2_update)
                ieee80211_send_layer2_update(sta);
 
        rcu_read_unlock();
@@ -949,6 +987,72 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
        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,
@@ -964,25 +1068,79 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
                return -EINVAL;
 
        if (params->use_cts_prot >= 0) {
-               sdata->bss_conf.use_cts_prot = params->use_cts_prot;
+               sdata->vif.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 =
+               sdata->vif.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 =
+               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 = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
@@ -1005,6 +1163,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,
 #endif
        .change_bss = ieee80211_change_bss,
+       .set_txq_params = ieee80211_set_txq_params,
+       .set_channel = ieee80211_set_channel,
 };