|
@@ -1593,6 +1593,37 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
|
|
return 0;
|
|
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,
|
|
static int ieee80211_remain_on_channel(struct wiphy *wiphy,
|
|
struct net_device *dev,
|
|
struct net_device *dev,
|
|
struct ieee80211_channel *chan,
|
|
struct ieee80211_channel *chan,
|
|
@@ -1601,16 +1632,62 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy,
|
|
u64 *cookie)
|
|
u64 *cookie)
|
|
{
|
|
{
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
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);
|
|
|
|
+ mutex_unlock(&local->mtx);
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
|
|
return ieee80211_wk_remain_on_channel(sdata, chan, channel_type,
|
|
return ieee80211_wk_remain_on_channel(sdata, chan, channel_type,
|
|
duration, cookie);
|
|
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,
|
|
static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
|
|
struct net_device *dev,
|
|
struct net_device *dev,
|
|
u64 cookie)
|
|
u64 cookie)
|
|
{
|
|
{
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
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);
|
|
return ieee80211_wk_cancel_remain_on_channel(sdata, cookie);
|
|
}
|
|
}
|
|
@@ -1662,6 +1739,12 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
|
|
channel_type != local->_oper_channel_type))
|
|
channel_type != local->_oper_channel_type))
|
|
is_offchan = true;
|
|
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)
|
|
if (is_offchan && !offchan)
|
|
return -EBUSY;
|
|
return -EBUSY;
|
|
|
|
|