Android wpa_supplicant源碼分析–掃描scan過程

1 掃描過程

一個完整的掃描過程 下發命令–>wpa_s構建掃描參數–>驅動掃描–>wpa_s接收到驅動的掃描結果

接收到framework/wpa_cli下發的SCAN命令

if (os_strncmp(buf, "SCAN ", 5) == 0) {
        wpas_ctrl_scan(wpa_s, buf + 5, reply, reply_size, &reply_len);

1.1 wpa_supplicant_scan

做一些檢測配置工作,然後將掃描的發起動作添加到radio_work中

wpa_supplicant_scan( wpa_supplicant *wpa_s )
{
    //檢查環境配置
    //
    !wpa_s->pno                                     //非pno狀態
    !wpa_s->wpa_state == WPA_INTERFACE_DISABLED     //網口在可用狀態
    //wpa_s中的scan一共有3種類型 NORMAL_SCAN_REQ wpa_s自主發起的,INITIAL_SCAN_REQ 對網口第一次發起的掃描, MANUAL_SCAN_REQ 上層命令發起的掃描
    //當前wifi爲斷開狀態,並且是wpa_s發起的掃描,則直接設置wpa_s狀態爲斷開,不再掃描
    !(wpa_s->disconnected && wpa_s->scan_req == NORMAL_SCAN_REQ)
        wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);

    //如果已經在掃描,則延後1秒再發起掃描
    if (wpa_s->scanning)
        wpa_supplicant_req_scan(wpa_s, 1, 0);

    //conf文件中沒有使能/可用的網絡,並且是wpa_s自主發起的掃描,則直接返回,並設置狀態
    (!wpa_supplicant_enabled_networks(wpa_s) && wpa_s->scan_req == NORMAL_SCAN_REQ))
        wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);

    //設置掃描類型爲正常掃描
    wpa_s->scan_req = NORMAL_SCAN_REQ;

    //設置wpa_s的狀態爲 SCANNING
    wpa_supplicant_set_state(wpa_s, WPA_SCANNING);

    //如果設置了autosan, 直接掃描
    if (wpa_s->autoscan_params != NULL)

    //如果該項不爲空,則直接關聯,跳過掃描步驟,在stat模式中沒有這一項,p2p中可能會用到
    if(wpa_s->connect_without_scan)
        wpa_supplicant_associate(wpa_s, NULL, ssid);

    //android中的掃描都是掃描所有,也可以掃描單個ssid
    wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;

    //wps和p2p會用到指定掃描的頻率
    wpa_supplicant_optimize_freqs(wpa_s, &params);

    //如果conf中定義了filter_bssid使能,那麼掃描結果中只包含conf中已有的ssid
    params.filter_ssids = wpa_supplicant_build_filter_ssids(wpa_s->conf, &params.num_filter_ssids);

    //觸發掃描
    ret = wpa_supplicant_trigger_scan(wpa_s, scan_params);
    {
        //添加到ridio work中, ridio work會在eloop循環中調用執行
        radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx)
        {
            //將scan用的raido work添加到 wpa_s radio的工作鏈表中
            dl_list_add_tail(&wpa_s->radio->work, &work->list);
        }
    }   
}

1.2 radio_work調用CB

當radio_work執行到wpas_trigger_scan_cb時,調用驅動中的掃描方法,然後通過初始化過程中創建的genl socket 下發掃描動作然後等待驅動處理

//驅動中的掃描
ret = wpa_drv_scan(wpa_s, params);
    wpa_s->driver->scan2(wpa_s->drv_priv, params)
        driver_nl80211_scan2
            wpa_driver_nl80211_scan(bss, params)            
{
    //生成nl msg
    struct nl_msg *msg = nl80211_scan_common(drv, NL80211_CMD_TRIGGER_SCAN, params, bss->wdev_id_set ? &bss->wdev_id : NULL);
    //通過nl發送消息,掃描返回的event接收到後再處理
    ret = send_and_recv_msgs(drv, msg, NULL, NULL);

    //如果驅動不支持上報掃描結果的event,那麼註冊超時10秒,如果支持,註冊超時30秒, 利用wpa_driver_nl80211_scan_timeout讀取掃描結果
    eloop_register_timeout(10/30, 0, wpa_driver_nl80211_scan_timeout,drv, drv->ctx);
    //最終會調用wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);接收掃描結果       
}

1.3 scan耗時統計

wpa_s中關於掃描統計了兩個耗時
1 下發驅動掃描命令到驅動執行掃描的時間

//正式運行radio work時間
radio_start_next_work()
    os_get_reltime(&now);
    work->cb(work, 0);
    wpas_trigger_scan_cb()
        os_get_reltime(&wpa_s->scan_trigger_time);

//下發驅動掃描後,接收到driver  EVENT_SCAN_STARTED(這個event就是爲了輸出log,沒有什麼實際的操作)
//計算開始掃描時間
os_get_reltime(&wpa_s->scan_start_time);
//輸出socket調用時間
os_reltime_sub(&wpa_s->scan_start_time, &wpa_s->scan_trigger_time, &diff);
wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds", diff.sec, diff.usec);

2 驅動執行掃描的耗時

    //計算dirver發送上EVENT_SCAN_STARTED 和 EVENT_SCAN_RESULTS 耗時
    os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
    wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds",diff.sec, diff.usec);

1.4 接收掃描結果

wpa_s接收到掃描結果,並保存在wpa_s bss鏈表中

wpa_supplicant_event(wpa_supplicant *wpa_s, EVENT_SCAN_RESULTS, NULL);
{
    //計算dirver發送上EVENT_SCAN_STARTED 和 EVENT_SCAN_RESULTS 耗時
    os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
    wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds",diff.sec, diff.usec);
    //獲取掃描結果, 如果返回值爲0,則成功獲取到掃描結果
    wpa_supplicant_event_scan_results()
    _wpa_supplicant_event_scan_results(wpa_s, data, 1)
    {
        //從驅動獲取掃描結果,
        struct wpa_scan_results *scan_res = wpa_supplicant_get_scan_results(wpa_s,  data ? &data->scan_info : NULL, 1);
        {
            //從驅動中獲取結果
            scan_res = wpa_drv_get_scan_results2(wpa_s);
                wpa_driver_nl80211_get_scan_results(void *priv)
            {
                //生成msg 並從驅動中獲取,bss_info_handler 爲處理結果的函數
                nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
                ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
            }

            //檢查wpa_s->bssid_filter,如果不爲空則進行filter
            filter_scan_res(wpa_s, scan_res);
            //進行一次排序
            qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *), compar);

            //for 更新每一個掃描結果,存儲到wpa_s bbs鏈表和 bssid中
            wpa_bss_update_scan_res(wpa_s, scan_res->res[i], &scan_res->fetch_time);
        }


        //處理下一次radio work
        radio_work_done(work);

        //一般來說,這裏不會選取網絡連接,忽略不計
        wpas_select_network_from_last_scan(wpa_s, 1, own_request);
        {
            //從掃描結果中選取一個bss網絡
            selected = wpa_supplicant_pick_network(wpa_s, &ssid(null));
            //判斷是否需要漫遊,這裏不太明白,沒看到匹配加密方式的,
            skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid);
            {               
                //根據當前連接的bss網絡信號強度和選擇的網絡信號強度的差值判斷是否需要漫遊
                //這是wpa_s自主決定的漫遊,有三個地方可以決定漫遊 framework wpa_s driver
                current_bss->level < -85 : min_diff = 1;
                current_bss->level < -80 : min_diff = 2;
                current_bss->level < -75 : min_diff = 3;
                current_bss->level < -70 : min_diff = 4;                
                current_bss->level > -70 : min_diff = 5;
            }
        }
    }   
}

1.5 流程圖

這裏寫圖片描述

2 連接過程

待補充

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章