Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 21f83589 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville
Browse files

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: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent c96e9635
Loading
Loading
Loading
Loading
+19 −0
Original line number Original line Diff line number Diff line
@@ -365,6 +365,7 @@ enum mac80211_tx_control_flags {
	IEEE80211_TX_INTFL_NL80211_FRAME_TX	= BIT(21),
	IEEE80211_TX_INTFL_NL80211_FRAME_TX	= BIT(21),
	IEEE80211_TX_CTL_LDPC			= BIT(22),
	IEEE80211_TX_CTL_LDPC			= BIT(22),
	IEEE80211_TX_CTL_STBC			= BIT(23) | BIT(24),
	IEEE80211_TX_CTL_STBC			= BIT(23) | BIT(24),
	IEEE80211_TX_CTL_TX_OFFCHAN		= BIT(25),
};
};


#define IEEE80211_TX_CTL_STBC_SHIFT		23
#define IEEE80211_TX_CTL_STBC_SHIFT		23
@@ -1824,6 +1825,12 @@ struct ieee80211_ops {
	int (*napi_poll)(struct ieee80211_hw *hw, int budget);
	int (*napi_poll)(struct ieee80211_hw *hw, int budget);
	int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
	int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
	int (*get_antenna)(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);
	int (*get_antenna)(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);

	int (*remain_on_channel)(struct ieee80211_hw *hw,
				 struct ieee80211_channel *chan,
				 enum nl80211_channel_type channel_type,
				 int duration);
	int (*cancel_remain_on_channel)(struct ieee80211_hw *hw);
};
};


/**
/**
@@ -2729,6 +2736,18 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,
 */
 */
void ieee80211_key_removed(struct ieee80211_key_conf *key_conf);
void ieee80211_key_removed(struct ieee80211_key_conf *key_conf);


/**
 * ieee80211_ready_on_channel - notification of remain-on-channel start
 * @hw: pointer as obtained from ieee80211_alloc_hw()
 */
void ieee80211_ready_on_channel(struct ieee80211_hw *hw);

/**
 * ieee80211_remain_on_channel_expired - remain_on_channel duration expired
 * @hw: pointer as obtained from ieee80211_alloc_hw()
 */
void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw);

/* Rate control API */
/* Rate control API */


/**
/**
+83 −0
Original line number Original line Diff line number Diff line
@@ -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;


+30 −0
Original line number Original line Diff line number Diff line
@@ -465,4 +465,34 @@ static inline int drv_get_antenna(struct ieee80211_local *local,
	return ret;
	return ret;
}
}


static inline int drv_remain_on_channel(struct ieee80211_local *local,
					struct ieee80211_channel *chan,
					enum nl80211_channel_type chantype,
					unsigned int duration)
{
	int ret;

	might_sleep();

	trace_drv_remain_on_channel(local, chan, chantype, duration);
	ret = local->ops->remain_on_channel(&local->hw, chan, chantype,
					    duration);
	trace_drv_return_int(local, ret);

	return ret;
}

static inline int drv_cancel_remain_on_channel(struct ieee80211_local *local)
{
	int ret;

	might_sleep();

	trace_drv_cancel_remain_on_channel(local);
	ret = local->ops->cancel_remain_on_channel(&local->hw);
	trace_drv_return_int(local, ret);

	return ret;
}

#endif /* __MAC80211_DRIVER_OPS */
#endif /* __MAC80211_DRIVER_OPS */
+80 −0
Original line number Original line Diff line number Diff line
@@ -933,6 +933,50 @@ TRACE_EVENT(drv_get_antenna,
	)
	)
);
);


TRACE_EVENT(drv_remain_on_channel,
	TP_PROTO(struct ieee80211_local *local, struct ieee80211_channel *chan,
		 enum nl80211_channel_type chantype, unsigned int duration),

	TP_ARGS(local, chan, chantype, duration),

	TP_STRUCT__entry(
		LOCAL_ENTRY
		__field(int, center_freq)
		__field(int, channel_type)
		__field(unsigned int, duration)
	),

	TP_fast_assign(
		LOCAL_ASSIGN;
		__entry->center_freq = chan->center_freq;
		__entry->channel_type = chantype;
		__entry->duration = duration;
	),

	TP_printk(
		LOCAL_PR_FMT " freq:%dMHz duration:%dms",
		LOCAL_PR_ARG, __entry->center_freq, __entry->duration
	)
);

TRACE_EVENT(drv_cancel_remain_on_channel,
	TP_PROTO(struct ieee80211_local *local),

	TP_ARGS(local),

	TP_STRUCT__entry(
		LOCAL_ENTRY
	),

	TP_fast_assign(
		LOCAL_ASSIGN;
	),

	TP_printk(
		LOCAL_PR_FMT, LOCAL_PR_ARG
	)
);

/*
/*
 * Tracing for API calls that drivers call.
 * Tracing for API calls that drivers call.
 */
 */
@@ -1170,6 +1214,42 @@ TRACE_EVENT(api_chswitch_done,
	)
	)
);
);


TRACE_EVENT(api_ready_on_channel,
	TP_PROTO(struct ieee80211_local *local),

	TP_ARGS(local),

	TP_STRUCT__entry(
		LOCAL_ENTRY
	),

	TP_fast_assign(
		LOCAL_ASSIGN;
	),

	TP_printk(
		LOCAL_PR_FMT, LOCAL_PR_ARG
	)
);

TRACE_EVENT(api_remain_on_channel_expired,
	TP_PROTO(struct ieee80211_local *local),

	TP_ARGS(local),

	TP_STRUCT__entry(
		LOCAL_ENTRY
	),

	TP_fast_assign(
		LOCAL_ASSIGN;
	),

	TP_printk(
		LOCAL_PR_FMT, LOCAL_PR_ARG
	)
);

/*
/*
 * Tracing for internal functions
 * Tracing for internal functions
 * (which may also be called in response to driver calls)
 * (which may also be called in response to driver calls)
+8 −0
Original line number Original line Diff line number Diff line
@@ -951,6 +951,13 @@ struct ieee80211_local {
	} debugfs;
	} debugfs;
#endif
#endif


	struct ieee80211_channel *hw_roc_channel;
	struct net_device *hw_roc_dev;
	struct work_struct hw_roc_start, hw_roc_done;
	enum nl80211_channel_type hw_roc_channel_type;
	unsigned int hw_roc_duration;
	u32 hw_roc_cookie;

	/* dummy netdev for use w/ NAPI */
	/* dummy netdev for use w/ NAPI */
	struct net_device napi_dev;
	struct net_device napi_dev;


@@ -1142,6 +1149,7 @@ void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local);
void ieee80211_offchannel_stop_station(struct ieee80211_local *local);
void ieee80211_offchannel_stop_station(struct ieee80211_local *local);
void ieee80211_offchannel_return(struct ieee80211_local *local,
void ieee80211_offchannel_return(struct ieee80211_local *local,
				 bool enable_beaconing);
				 bool enable_beaconing);
void ieee80211_hw_roc_setup(struct ieee80211_local *local);


/* interface handling */
/* interface handling */
int ieee80211_iface_init(void);
int ieee80211_iface_init(void);
Loading