mac80211: implement hardware offload for remain-on-channel

This allows drivers to support remain-on-channel
offload if they implement smarter timing or need
to use a device implementation like iwlwifi.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Johannes Berg
2010-12-18 17:20:47 +01:00
committed by John W. Linville
parent c96e96354a
commit 21f8358964
8 changed files with 306 additions and 3 deletions

View File

@@ -1593,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,
@@ -1601,16 +1632,62 @@ 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);
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);
}
@@ -1662,6 +1739,12 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
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;