Hi3516如何連線Wifi(二)

HarmonyOS技術社群發表於2021-03-30

目錄:

一、總體思路

二、啟動Daemon

三、作者文章合集

書承上回(Hi3516如何連線Wifi(一)),上一篇聊了一下怎樣在Hi3516中用wpa_supplicant連線到Wifi熱點,本文講一下如何通過程式設計實現。

一、總體思路

    首先我們需要搞清楚Hi3516中Wifi的相關模組,以及他們之間的關係,其實和linux是很相似的。首先,我們需要執行一個的Daemon,也就是上文提到的wpa_supplicant,負責對網路卡的硬體呼叫,比如連線wifi、斷開wifi、啟動熱點等等。這個Daemon開放一個socket埠,外部程式可以通過本地連線向其傳送指令實現間接對wifi的呼叫,這無疑是給我們提供了很大的便利,不用從底層重新造輪子了。

    鴻蒙OS程式碼中,有一個示例,在//applications/sample/camera/communication/wpa_cli,實現了連線Daemon、掃描熱點、連線熱點等功能。

    現在方案就很明確了,第一啟動Daemon,第二向Daemon傳送命令。下面我們就來詳細分析如何實現。

二、啟動Daemon

    檢視程式碼//applications/sample/camera/communication/wpa_supplicant/src/wpa_sample.c,找到main函式,發現它只做了一件事情,那就是呼叫pthread_create建立了一個執行緒,執行緒執行的函式是ThreadMain。而ThreadMain也只做了一件事情,那就是載入/usr/lib/libwpa.so,然後執行了其中的wpa_main函式,同時把命令列引數傳遞了進去。而wpa_main函式具體呼叫網路卡就是通過hdf框架向核心態傳送訊息了,這裡就不再贅述。

static void* ThreadMain()
{
    printf("[WpaSample]init wpa_supplicant.\n");

    void *handleLibWpa = dlopen("/usr/lib/libwpa.so", RTLD_NOW | RTLD_LOCAL);
    if (handleLibWpa == NULL) {
        printf("[WpaSample]dlopen libwpa failed.\n");
        return NULL;
    }
    int (*func)(int, char **) = NULL;
    func =  dlsym(handleLibWpa, "wpa_main");
    if (func == NULL) {
        dlclose(handleLibWpa);
        printf("[WpaSample]dlsym wpa_main failed.\n");
        return NULL;
    }
    int ret = func(g_wpaArgc, g_wpaArg);

    printf("[WpaSample]run wpa_main failed, ret:%d.\n", ret);
    for (int i = 0; i < g_wpaArgc; i++) {
        printf("[WpaSample]arg %d:%s.\n", i, g_wpaArg[i]);
    }

    if (dlclose(handleLibWpa) != 0) {
        printf("[WpaSample]dlclose libwpa failed.\n");
        return NULL;
    }
    return NULL;
}

int main(int argc, char *argv[])
{
    g_wpaArgc = argc;
    for (int i = 0; i < g_wpaArgc; i++) {
        g_wpaArg[i] = argv[i];
    }

    int ret = pthread_create(&g_wpaThread, NULL, ThreadMain, NULL);
    if (ret != 0) {
        printf("[WpaSample]create thread failed error:%s.\n", strerror(ret));
        return 1;
    }
    pthread_join(g_wpaThread, NULL);
    return 0;
}

 

    我們要做的就是仿照main函式寫自己的程式碼,把引數固定就可以了。我們的引數是這樣的:g_wpaArg[0]="",g_wpaArg[1]="-iwlan0",g_wpaArg[2]="-c/etc/wpa_supplicant.conf",其中第0個引數是可執行檔案的名稱,這裡可以隨意填或者直接留空。

    如果只是想啟動Daemon,不連線到任何Wifi熱點,那這裡第二個引數-c指向的.conf檔案中,不應該包含ssid和psk,也就是直接使用系統自帶的預設conf就可以。我們在上一篇文章中修改了wpa_supplicant.conf,加入了ssid和psk。這裡做一個改進,原wpa_supplicant.conf保持不變,新增一個wpa_supplicant_(你的熱點名稱).conf,加入ssid和psk,然後修改//applications/sample/camera/communication/wpa_supplicant/BUILD.gn,新增需要copy的檔案:

copy("config2") {
    sources = [
        "config/wpa_supplicant_(xxx).conf"
    ]
    outputs = [
        "$root_out_dir/etc/wpa_supplicant_(xxx).conf"
    ]
}

 

    這樣我們wpa_supplicant就有兩種操作了,只啟動Daemon,和啟動Daemon且連線到指定熱點,只需要改變-c指定的conf檔案。

    補充一下,我曾嘗試過使用system函式執行wpa_supplicant的方式啟動Daemon,但是失敗了,原因是鴻蒙暫時還不支援system函式。具體可以看一下system的程式碼實現\\third_party\musl\src\process\system.c

int system(const char *cmd)
{
    pid_t pid;
    sigset_t old, reset;
    struct sigaction sa = { .sa_handler = SIG_IGN }, oldint, oldquit;
    int status = -1, ret;
    posix_spawnattr_t attr;

    unsupported_api(__FUNCTION__);//不受支援的api
...

 

    另外,嘗試了用fork建立執行緒也是可行的。

    還有一個檔案許可權問題。如果你用上述方法編寫一個控制檯程式來執行是沒有問題的,無非就是重寫了一個sample裡的wpa_supplicant。但是當你在hap中通過ace呼叫時就出現了錯誤:

OHOS # 01-01 00:40:03.661 17 59 I 03900/ACE: InitWifi invoked!
[WpaSample]init wpa_supplicant.
01-01 00:40:03.661 17 59 I 03900/ACE: InitDaemon2
Successfully initialized wpa_supplicant
[HDF:E/hdf_syscall_adapter]Open file node failed: /dev/hdfwifi
[HDF:E/HDF_LOG_TAG]WpaMsgServiceInit: fail to get remote service!

 

    看樣子是開啟/dev/hdfwifi失敗了,這個問題我研究了很久,最後意識到hap的執行使用者可能和shell不同,shell是root使用者在執行,而hap肯定不是root在執行,這導致了許可權不足。我看了一下/dev/hdfwifi的許可權

OHOS # ls /dev
Directory /dev:
(略)
-rw-rw-r-- 0        u:0     g:99    hdfwifi
(略)
 

    其他使用者是r許可權,顯然我們也需要w許可權。執行chmod 0666 /dev/hdfwifi就可以了,但燒寫後通過連線shell做這件事很不方便,最好能自動化。

    這裡我們可以藉助鴻蒙系統初始化階段執行的job來實現我們的目的,在\\base\startup\services\init_lite\src\main.c負責執行系統啟動後的任務,包括各種job和service,job分為pre-init,init,post-init三個階段。具體要執行哪些命令,都寫在\\vendor\huawei\camera\init_configs\init_liteos_a_3516dv300.cfg配置檔案中,我們要做的就是在job中找到post-init,然後在cmds新增我們的指令chmod 0666 /dev/hdfwifi

{
    "jobs" : [{
            "name" : "pre-init",
            "cmds" : [
                "mkdir /storage/data/log",
                (略)
            ]
        }, {
            "name" : "init",
            "cmds" : [
                "start shell",
                (略)
            ]
        }, {
            "name" : "post-init",
            "cmds" : [
                "chown 0 99 /dev/dev_mgr",
                "chown 0 99 /dev/hdfwifi",
                "chmod 0666 /dev/hdfwifi",//這裡
 

下一篇再將如何連線Daemon,真正實現連線Wifi,以及如何通過ACE在UI介面中操作連線Wifi。

作者:老船伕

想了解更多內容,請訪問51CTO和華為合作共建的鴻蒙社群:https://harmonyos.51cto.com

相關文章