Hi3516如何連接Wifi(二)
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
書承上回(Hi3516如何連接Wifi(一)),上一篇聊了一下怎樣在Hi3516中用wpa_supplicant連接到Wifi熱點(diǎn),本文講一下如何通過編程實(shí)現(xiàn)。
一、總體思路
首先我們需要搞清楚Hi3516中Wifi的相關(guān)模塊,以及他們之間的關(guān)系,其實(shí)和linux是很相似的。首先,我們需要運(yùn)行一個(gè)的Daemon,也就是上文提到的wpa_supplicant,負(fù)責(zé)對網(wǎng)卡的硬件調(diào)用,比如連接wifi、斷開wifi、啟動熱點(diǎn)等等。這個(gè)Daemon開放一個(gè)socket端口,外部程序可以通過本地連接向其發(fā)送指令實(shí)現(xiàn)間接對wifi的調(diào)用,這無疑是給我們提供了很大的便利,不用從底層重新造輪子了。
鴻蒙OS代碼中,有一個(gè)示例,在//applications/sample/camera/communication/wpa_cli,實(shí)現(xiàn)了連接Daemon、掃描熱點(diǎn)、連接熱點(diǎn)等功能。
現(xiàn)在方案就很明確了,第一啟動Daemon,第二向Daemon發(fā)送命令。下面我們就來詳細(xì)分析如何實(shí)現(xiàn)。
二、啟動Daemon
查看代碼//applications/sample/camera/communication/wpa_supplicant/src/wpa_sample.c,找到main函數(shù),發(fā)現(xiàn)它只做了一件事情,那就是調(diào)用pthread_create創(chuàng)建了一個(gè)線程,線程執(zhí)行的函數(shù)是ThreadMain。而ThreadMain也只做了一件事情,那就是加載/usr/lib/libwpa.so,然后執(zhí)行了其中的wpa_main函數(shù),同時(shí)把命令行參數(shù)傳遞了進(jìn)去。而wpa_main函數(shù)具體調(diào)用網(wǎng)卡就是通過hdf框架向內(nèi)核態(tài)發(fā)送消息了,這里就不再贅述。
- 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函數(shù)寫自己的代碼,把參數(shù)固定就可以了。我們的參數(shù)是這樣的:g_wpaArg[0]="",g_wpaArg[1]="-iwlan0",g_wpaArg[2]="-c/etc/wpa_supplicant.conf",其中第0個(gè)參數(shù)是可執(zhí)行文件的名稱,這里可以隨意填或者直接留空。
如果只是想啟動Daemon,不連接到任何Wifi熱點(diǎn),那這里第二個(gè)參數(shù)-c指向的.conf文件中,不應(yīng)該包含ssid和psk,也就是直接使用系統(tǒng)自帶的默認(rèn)conf就可以。我們在上一篇文章中修改了wpa_supplicant.conf,加入了ssid和psk。這里做一個(gè)改進(jìn),原wpa_supplicant.conf保持不變,新增一個(gè)wpa_supplicant_(你的熱點(diǎn)名稱).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且連接到指定熱點(diǎn),只需要改變-c指定的conf文件。
補(bǔ)充一下,我曾嘗試過使用system函數(shù)執(zhí)行wpa_supplicant的方式啟動Daemon,但是失敗了,原因是鴻蒙暫時(shí)還不支持system函數(shù)。具體可以看一下system的代碼實(shí)現(xiàn)\\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創(chuàng)建線程也是可行的。
還有一個(gè)文件權(quán)限問題。如果你用上述方法編寫一個(gè)控制臺程序來運(yùn)行是沒有問題的,無非就是重寫了一個(gè)sample里的wpa_supplicant。但是當(dāng)你在hap中通過ace調(diào)用時(shí)就出現(xiàn)了錯誤:
- 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失敗了,這個(gè)問題我研究了很久,最后意識到hap的執(zhí)行用戶可能和shell不同,shell是root用戶在執(zhí)行,而hap肯定不是root在執(zhí)行,這導(dǎo)致了權(quán)限不足。我看了一下/dev/hdfwifi的權(quán)限
- OHOS # ls /dev
- Directory /dev:
- (略)
- -rw-rw-r-- 0 u:0 g:99 hdfwifi
- (略)
其他用戶是r權(quán)限,顯然我們也需要w權(quán)限。執(zhí)行chmod 0666 /dev/hdfwifi就可以了,但燒寫后通過連接shell做這件事很不方便,最好能自動化。
這里我們可以借助鴻蒙系統(tǒng)初始化階段執(zhí)行的job來實(shí)現(xiàn)我們的目的,在\\base\startup\services\init_lite\src\main.c負(fù)責(zé)執(zhí)行系統(tǒng)啟動后的任務(wù),包括各種job和service,job分為pre-init,init,post-init三個(gè)階段。具體要執(zhí)行哪些命令,都寫在\\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,真正實(shí)現(xiàn)連接Wifi,以及如何通過ACE在UI界面中操作連接Wifi。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)