cfg80211: require add_virtual_intf to return new dev
[deliverable/linux.git] / net / mac80211 / cfg.c
index 18bd0e550600fccac46a106d3f29ffd7d609ce5b..d34c7c3dd762c2b485b424ba29d92601fba34801 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)
@@ -1024,6 +1028,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))
@@ -1299,6 +1305,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);
 
@@ -1544,27 +1557,54 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
        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 (is_offchan && !offchan)
                return -EBUSY;
 
        switch (sdata->vif.type) {
@@ -1598,12 +1638,70 @@ 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;
+
+       /*
+        * 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);
+       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 +1719,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,
@@ -1671,6 +1786,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,
 };
This page took 0.028598 seconds and 5 git commands to generate.