您的位置:首页 > 其它

mac80211解析六

2017-02-22 23:24 363 查看
mac80211的扫描请求由用户空间通过nl80211发起,调用了mac80211中的
ieee80211_scan
,该函数内容如下:

static int ieee80211_scan(struct wiphy *wiphy,
struct cfg80211_scan_request *req)
{
struct ieee80211_sub_if_data *sdata;

sdata = IEEE80211_WDEV_TO_SUB_IF(req->wdev);

switch (ieee80211_vif_type_p2p(&sdata->vif)) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_DEVICE:
break;
case NL80211_IFTYPE_P2P_GO:
if (sdata->local->ops->hw_scan)
break;
/*
* FIXME: implement NoA while scanning in software,
* for now fall through to allow scanning only when
* beaconing hasn't been configured yet
*/
case NL80211_IFTYPE_AP:
/*
* If the scan has been forced (and the driver supports
* forcing), don't care about being beaconing already.
* This will create problems to the attached stations (e.g. all
* the  frames sent while scanning on other channel will be
* lost)
*/
if (sdata->u.ap.beacon &&
(!(wiphy->features & NL80211_FEATURE_AP_SCAN) ||
!(req->flags & NL80211_SCAN_FLAG_AP)))
return -EOPNOTSUPP;
break;
default:
return -EOPNOTSUPP;
}

return ieee80211_request_scan(sdata, req);
}


经过选择扫描接口类型之后,调用
ieee80211_request_scan
函数,在进一步使用
__ieee80211_start_scan
函数实现扫描:

static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
struct cfg80211_scan_request *req)
{
struct ieee80211_local *local = sdata->local;
int rc;

lockdep_assert_held(&local->mtx);

if (local->scan_req)
return -EBUSY;

if (!ieee80211_can_scan(local, sdata)) {
/* wait for the work to finish/time out */
local->scan_req = req;
rcu_assign_pointer(local->scan_sdata, sdata);
return 0;
}

if (local->ops->hw_scan) {
u8 *ies;

local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len;
local->hw_scan_req = kmalloc(
sizeof(*local->hw_scan_req) +
req->n_channels * sizeof(req->channels[0]) +
local->hw_scan_ies_bufsize, GFP_KERNEL);
if (!local->hw_scan_req)
return -ENOMEM;

local->hw_scan_req->ssids = req->ssids;
local->hw_scan_req->n_ssids = req->n_ssids;
ies = (u8 *)local->hw_scan_req +
sizeof(*local->hw_scan_req) +
req->n_channels * sizeof(req->channels[0]);
local->hw_scan_req->ie = ies;
local->hw_scan_req->flags = req->flags;

local->hw_scan_band = 0;

/*
* After allocating local->hw_scan_req, we must
* go through until ieee80211_prep_hw_scan(), so
* anything that might be changed here and leave
* this function early must not go after this
* allocation.
*/
}

local->scan_req = req;
rcu_assign_pointer(local->scan_sdata, sdata);

if (local->ops->hw_scan) {
__set_bit(SCAN_HW_SCANNING, &local->scanning);
} else if ((req->n_channels == 1) &&
(req->channels[0] == local->_oper_chandef.chan)) {
/*
* If we are scanning only on the operating channel
* then we do not need to stop normal activities
*/
unsigned long next_delay;

__set_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning);

ieee80211_recalc_idle(local);

/* Notify driver scan is starting, keep order of operations
* same as normal software scan, in case that matters. */
drv_sw_scan_start(local);

ieee80211_configure_filter(local); /* accept probe-responses */

/* We need to ensure power level is at max for scanning. */
ieee80211_hw_config(local, 0);

if ((req->channels[0]->flags &
IEEE80211_CHAN_NO_IR) ||
!local->scan_req->n_ssids) {
next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
} else {
ieee80211_scan_state_send_probe(local, &next_delay);
next_delay = IEEE80211_CHANNEL_TIME;
}

/* Now, just wait a bit and we are all done! */
ieee80211_queue_delayed_work(&local->hw, &local->scan_work,
next_delay);
return 0;
} else {
/* Do normal software scan */
__set_bit(SCAN_SW_SCANNING, &local->scanning);
}

ieee80211_recalc_idle(local);

if (local->ops->hw_scan) {
WARN_ON(!ieee80211_prep_hw_scan(local));
rc = drv_hw_scan(local, sdata, local->hw_scan_req);
} else
rc = ieee80211_start_sw_scan(local);

if (rc) {
kfree(local->hw_scan_req);
local->hw_scan_req = NULL;
local->scanning = 0;

ieee80211_recalc_idle(local);

local->scan_req = NULL;
RCU_INIT_POINTER(local->scan_sdata, NULL);
}

return rc;
}


如果支持硬件扫描,最后通过
drv_hw_scan
函数调用
local->ops->hw_scan()
执行硬件扫描,否则,调用
ieee80211_start_sw_scan
函数进行软件扫描。

static int ieee80211_start_sw_scan(struct ieee80211_local *local)
{
/* Software scan is not supported in multi-channel cases */
if (local->use_chanctx)
return -EOPNOTSUPP;

/*
* Hardware/driver doesn't support hw_scan, so use software
* scanning instead. First send a nullfunc frame with power save
* bit on so that AP will buffer the frames for us while we are not
* listening, then send probe requests to each channel and wait for
* the responses. After all channels are scanned, tune back to the
* original channel and send a nullfunc frame with power save bit
* off to trigger the AP to send us all the buffered frames.
*
* Note that while local->sw_scanning is true everything else but
* nullfunc frames and probe requests will be dropped in
* ieee80211_tx_h_check_assoc().
*/
drv_sw_scan_start(local);

local->leave_oper_channel_time = jiffies;
local->next_scan_state = SCAN_DECISION;
local->scan_channel_idx = 0;

ieee80211_offchannel_stop_vifs(local);

/* ensure nullfunc is transmitted before leaving operating channel */
ieee80211_flush_queues(local, NULL);

ieee80211_configure_filter(local);

/* We need to set power level at maximum rate for scanning. */
ieee80211_hw_config(local, 0);

ieee80211_queue_delayed_work(&local->hw,
&local->scan_work, 0);

return 0;
}


延时唤醒
ieee80211_scan_work()
函数:

void ieee80211_scan_work(struct work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, scan_work.work);
struct ieee80211_sub_if_data *sdata;
unsigned long next_delay = 0;
bool aborted;

mutex_lock(&local->mtx);

sdata = rcu_dereference_protected(local->scan_sdata,
lockdep_is_held(&local->mtx));

/* When scanning on-channel, the first-callback means completed. */
if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) {
aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning);
goto out_complete;
}

if (test_and_clear_bit(SCAN_COMPLETED, &local->scanning)) {
aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning);
goto out_complete;
}

if (!sdata || !local->scan_req)
goto out;

if (local->scan_req && !local->scanning) {
struct cfg80211_scan_request *req = local->scan_req;
int rc;

local->scan_req = NULL;
RCU_INIT_POINTER(local->scan_sdata, NULL);

rc = __ieee80211_start_scan(sdata, req);
if (rc) {
/* need to complete scan in cfg80211 */
local->scan_req = req;
aborted = true;
goto out_complete;
} else
goto out;
}

/*
* as long as no delay is required advance immediately
* without scheduling a new work
*/
do {
if (!ieee80211_sdata_running(sdata)) {
aborted = true;
goto out_complete;
}

switch (local->next_scan_state) {
case SCAN_DECISION:
/* if no more bands/channels left, complete scan */
if (local->scan_channel_idx >= local->scan_req->n_channels) {
aborted = false;
goto out_complete;
}
ieee80211_scan_state_decision(local, &next_delay);
break;
case SCAN_SET_CHANNEL:
ieee80211_scan_state_set_channel(local, &next_delay);
break;
case SCAN_SEND_PROBE:
ieee80211_scan_state_send_probe(local, &next_delay);
break;
case SCAN_SUSPEND:
ieee80211_scan_state_suspend(local, &next_delay);
break;
case SCAN_RESUME:
ieee80211_scan_state_resume(local, &next_delay);
break;
case SCAN_ABORT:
aborted = true;
goto out_complete;
}
} while (next_delay == 0);

ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay);
goto out;

out_complete:
__ieee80211_scan_completed(&local->hw, aborted);
out:
mutex_unlock(&local->mtx);
}


根据
next_scan_state
调用相应的处理函数,果
next_delay==0
,则继续根据
next_scan_state
调用相应的处理函数。

mac80211中扫描状态机路径和扫描请求的路径相似,不同的是如果存在硬件扫描请求,调用
drv_hw_scan()
进行扫描,如果失败,调用

ieee80211_scan_completed()
完成扫描

void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
{
struct ieee80211_local *local = hw_to_local(hw);

trace_api_scan_completed(local, aborted);

set_bit(SCAN_COMPLETED, &local->scanning);
if (aborted)
set_bit(SCAN_ABORTED, &local->scanning);
ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0);
}


如果存在扫描请求,同时未进行扫描,调用
__ieee80211_start_scan()
进行软件扫描,如果失败,调用
ieee80211_scan_completed()
完成扫描

static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
{
struct ieee80211_local *local = hw_to_local(hw);
bool hw_scan = local->ops->hw_scan;
bool was_scanning = local->scanning;

lockdep_assert_held(&local->mtx);

/*
* It's ok to abort a not-yet-running scan (that
* we have one at all will be verified by checking
* local->scan_req next), but not to complete it
* successfully.
*/
if (WARN_ON(!local->scanning && !aborted))
aborted = true;

if (WARN_ON(!local->scan_req))
return;

if (hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
int rc;

rc = drv_hw_scan(local,
rcu_dereference_protected(local->scan_sdata,
lockdep_is_held(&local->mtx)),
local->hw_scan_req);

if (rc == 0)
return;
}

kfree(local->hw_scan_req);
local->hw_scan_req = NULL;

if (local->scan_req != local->int_scan_req)
cfg80211_scan_done(local->scan_req, aborted);
local->scan_req = NULL;
RCU_INIT_POINTER(local->scan_sdata, NULL);

local->scanning = 0;
local->scan_chandef.chan = NULL;

/* Set power back to normal operating levels. */
ieee80211_hw_config(local, 0);

if (!hw_scan) {
ieee80211_configure_filter(local);
drv_sw_scan_complete(local);
ieee80211_offchannel_return(local);
}

ieee80211_recalc_idle(local);

ieee80211_mlme_notify_scan_completed(local);
ieee80211_ibss_notify_scan_completed(local);
ieee80211_mesh_notify_scan_completed(local);
if (was_scanning)
ieee80211_start_next_roc(local);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息