Skip to content

Commit

Permalink
cfg80211: add remain-on-channel command
Browse files Browse the repository at this point in the history
Add new commands for requesting the driver to remain awake
on a specified channel for the specified amount of time
(and another command to cancel such an operation). This
can be used to implement userspace-controlled off-channel
operations, like Public Action frame exchange on another
channel than the operation channel.

The off-channel operation should behave similarly to scan,
i.e. the local station (if associated) moves into power
save mode to request the AP to buffer frames for it and
then moves to the other channel to allow the off-channel
operation to be completed. The duration parameter can be
used to request enough time to receive a response from
the target station.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Jouni Malinen authored and John W. Linville committed Dec 28, 2009
1 parent b203ffc commit 9588bbd
Show file tree
Hide file tree
Showing 7 changed files with 368 additions and 15 deletions.
36 changes: 36 additions & 0 deletions include/linux/nl80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,31 @@
* @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices
* associated with this wiphy must be down and will follow.
*
* @NL80211_CMD_REMAIN_ON_CHANNEL: Request to remain awake on the specified
* channel for the specified amount of time. This can be used to do
* off-channel operations like transmit a Public Action frame and wait for
* a response while being associated to an AP on another channel.
* %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify which
* radio is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the
* frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be
* optionally used to specify additional channel parameters.
* %NL80211_ATTR_DURATION is used to specify the duration in milliseconds
* to remain on the channel. This command is also used as an event to
* notify when the requested duration starts (it may take a while for the
* driver to schedule this time due to other concurrent needs for the
* radio).
* When called, this operation returns a cookie (%NL80211_ATTR_COOKIE)
* that will be included with any events pertaining to this request;
* the cookie is also used to cancel the request.
* @NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: This command can be used to cancel a
* pending remain-on-channel duration if the desired operation has been
* completed prior to expiration of the originally requested duration.
* %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify the
* radio. The %NL80211_ATTR_COOKIE attribute must be given as well to
* uniquely identify the request.
* This command is also used as an event to notify when a requested
* remain-on-channel duration has expired.
*
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
Expand Down Expand Up @@ -353,6 +378,9 @@ enum nl80211_commands {
NL80211_CMD_DEL_PMKSA,
NL80211_CMD_FLUSH_PMKSA,

NL80211_CMD_REMAIN_ON_CHANNEL,
NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,

/* add new commands above here */

/* used to define NL80211_CMD_MAX below */
Expand Down Expand Up @@ -606,6 +634,10 @@ enum nl80211_commands {
* @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can
* cache, a wiphy attribute.
*
* @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32.
*
* @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects.
*
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
Expand Down Expand Up @@ -743,6 +775,10 @@ enum nl80211_attrs {
NL80211_ATTR_PMKID,
NL80211_ATTR_MAX_NUM_PMKIDS,

NL80211_ATTR_DURATION,

NL80211_ATTR_COOKIE,

/* add attributes here, update the policy in nl80211.c */

__NL80211_ATTR_AFTER_LAST,
Expand Down
47 changes: 47 additions & 0 deletions include/net/cfg80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,15 @@ struct cfg80211_pmksa {
*
* @dump_survey: get site survey information.
*
* @remain_on_channel: Request the driver to remain awake on the specified
* channel for the specified duration to complete an off-channel
* operation (e.g., public action frame exchange). When the driver is
* ready on the requested channel, it must indicate this with an event
* notification by calling cfg80211_ready_on_channel().
* @cancel_remain_on_channel: Cancel an on-going remain-on-channel operation.
* This allows the operation to be terminated prior to timeout based on
* the duration value.
*
* @testmode_cmd: run a test mode command
*
* @set_pmksa: Cache a PMKID for a BSSID. This is mostly useful for fullmac
Expand Down Expand Up @@ -1123,6 +1132,16 @@ struct cfg80211_ops {
struct cfg80211_pmksa *pmksa);
int (*flush_pmksa)(struct wiphy *wiphy, struct net_device *netdev);

int (*remain_on_channel)(struct wiphy *wiphy,
struct net_device *dev,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type,
unsigned int duration,
u64 *cookie);
int (*cancel_remain_on_channel)(struct wiphy *wiphy,
struct net_device *dev,
u64 cookie);

/* some temporary stuff to finish wext */
int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev,
bool enabled, int timeout);
Expand Down Expand Up @@ -2147,5 +2166,33 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid,
void cfg80211_disconnected(struct net_device *dev, u16 reason,
u8 *ie, size_t ie_len, gfp_t gfp);

/**
* cfg80211_ready_on_channel - notification of remain_on_channel start
* @dev: network device
* @cookie: the request cookie
* @chan: The current channel (from remain_on_channel request)
* @channel_type: Channel type
* @duration: Duration in milliseconds that the driver intents to remain on the
* channel
* @gfp: allocation flags
*/
void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type,
unsigned int duration, gfp_t gfp);

/**
* cfg80211_remain_on_channel_expired - remain_on_channel duration expired
* @dev: network device
* @cookie: the request cookie
* @chan: The current channel (from remain_on_channel request)
* @channel_type: Channel type
* @gfp: allocation flags
*/
void cfg80211_remain_on_channel_expired(struct net_device *dev,
u64 cookie,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type,
gfp_t gfp);

#endif /* __NET_CFG80211_H */
41 changes: 27 additions & 14 deletions net/wireless/chan.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,44 +41,57 @@ rdev_fixed_channel(struct cfg80211_registered_device *rdev,
return result;
}

int rdev_set_freq(struct cfg80211_registered_device *rdev,
struct wireless_dev *for_wdev,
struct ieee80211_channel *
rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
int freq, enum nl80211_channel_type channel_type)
{
struct ieee80211_channel *chan;
struct ieee80211_sta_ht_cap *ht_cap;
int result;

if (rdev_fixed_channel(rdev, for_wdev))
return -EBUSY;

if (!rdev->ops->set_channel)
return -EOPNOTSUPP;

chan = ieee80211_get_channel(&rdev->wiphy, freq);

/* Primary channel not allowed */
if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
return -EINVAL;
return NULL;

if (channel_type == NL80211_CHAN_HT40MINUS &&
chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
return -EINVAL;
return NULL;
else if (channel_type == NL80211_CHAN_HT40PLUS &&
chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
return -EINVAL;
return NULL;

ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;

if (channel_type != NL80211_CHAN_NO_HT) {
if (!ht_cap->ht_supported)
return -EINVAL;
return NULL;

if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
return -EINVAL;
return NULL;
}

return chan;
}

int rdev_set_freq(struct cfg80211_registered_device *rdev,
struct wireless_dev *for_wdev,
int freq, enum nl80211_channel_type channel_type)
{
struct ieee80211_channel *chan;
int result;

if (rdev_fixed_channel(rdev, for_wdev))
return -EBUSY;

if (!rdev->ops->set_channel)
return -EOPNOTSUPP;

chan = rdev_freq_to_chan(rdev, freq, channel_type);
if (!chan)
return -EINVAL;

result = rdev->ops->set_channel(&rdev->wiphy, chan, channel_type);
if (result)
return result;
Expand Down
3 changes: 3 additions & 0 deletions net/wireless/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,9 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
struct ieee80211_channel *
rdev_fixed_channel(struct cfg80211_registered_device *rdev,
struct wireless_dev *for_wdev);
struct ieee80211_channel *
rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
int freq, enum nl80211_channel_type channel_type);
int rdev_set_freq(struct cfg80211_registered_device *rdev,
struct wireless_dev *for_wdev,
int freq, enum nl80211_channel_type channel_type);
Expand Down
27 changes: 27 additions & 0 deletions net/wireless/mlme.c
Original file line number Diff line number Diff line change
Expand Up @@ -680,3 +680,30 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
}
}
}

void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type,
unsigned int duration, gfp_t gfp)
{
struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);

nl80211_send_remain_on_channel(rdev, dev, cookie, chan, channel_type,
duration, gfp);
}
EXPORT_SYMBOL(cfg80211_ready_on_channel);

void cfg80211_remain_on_channel_expired(struct net_device *dev,
u64 cookie,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type,
gfp_t gfp)
{
struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);

nl80211_send_remain_on_channel_cancel(rdev, dev, cookie, chan,
channel_type, gfp);
}
EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
Loading

0 comments on commit 9588bbd

Please sign in to comment.