]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/mac80211/cfg.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / net / mac80211 / cfg.c
index 18bd0e550600fccac46a106d3f29ffd7d609ce5b..9cd73b11506e85b1a0d5ecf26563da8d1d439d18 100644 (file)
 #include "rate.h"
 #include "mesh.h"
 
-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;
@@ -29,12 +30,15 @@ static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
        int err;
 
        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)
@@ -56,11 +60,6 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
        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);
@@ -296,11 +295,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;
 }
@@ -343,8 +343,9 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
 
        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;
@@ -983,7 +984,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)
 {
@@ -999,9 +1000,39 @@ 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;
+
+       /* first allocate the new vendor information element */
+       new_ie = NULL;
+       old_ie = ifmsh->vendor_ie;
+
+       ifmsh->vendor_ie_len = setup->vendor_ie_len;
+       if (setup->vendor_ie_len) {
+               new_ie = kmemdup(setup->vendor_ie, setup->vendor_ie_len,
+                               GFP_KERNEL);
+               if (!new_ie)
+                       return -ENOMEM;
+       }
+
+       /* 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->vendor_ie = new_ie;
+
+       kfree(old_ie);
+
+       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;
@@ -1024,6 +1055,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))
@@ -1050,6 +1083,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,
@@ -1108,6 +1166,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;
@@ -1299,6 +1363,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);
 
@@ -1522,6 +1593,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,
@@ -1530,41 +1632,121 @@ 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 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,
+                            struct ieee80211_channel *chan, bool offchan,
                             enum nl80211_channel_type channel_type,
-                            bool channel_type_valid,
+                            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) {
@@ -1572,6 +1754,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
        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;
@@ -1598,12 +1781,130 @@ static int ieee80211_mgmt_tx(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->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->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->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)
@@ -1621,6 +1922,23 @@ static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
        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);
+}
+
 struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
@@ -1645,8 +1963,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,
@@ -1671,6 +1991,9 @@ struct cfg80211_ops mac80211_config_ops = {
        .remain_on_channel = ieee80211_remain_on_channel,
        .cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
        .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,
 };