天天看點

mac80211解析六

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 ;
    }

    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[]) +
                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[]);
        local->hw_scan_req->ie = ies;
        local->hw_scan_req->flags = req->flags;

        local->hw_scan_band = ;

        /*
         * 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 == ) &&
           (req->channels[] == 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, );

        if ((req->channels[]->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 ;
    } 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 = ;

        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 = ;

    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, );

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

    return ;
}
           

延時喚醒

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 = ;
    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 == );

    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, );
}
           

如果存在掃描請求,同時未進行掃描,調用

__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 == )
            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 = ;
    local->scan_chandef.chan = NULL;

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

    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);
}
           

繼續閱讀