Hi3516如何連接Wifi(二)
目錄:
書承上回(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和華為合作共建的鴻蒙社區://harmonyos.51cto.com