android手動連接wifi的過程

android手動連接wifi的過程

下面就以手動連接mtk5931的wifi爲列,來說明手動連接wifi的過程。
在此之前,先說明下,手動連接的使用場景和作用:
a: 在純linux的環境下,該手動連接過程,同樣適用
b: 在wifi驅動的移植初期,可以通過手動連接來測試該款wifi的基本驅動和wpa_supplicant是否工作正常
c: 通過手動連接,你可以很容易瞭解wifi的工作過程
d: 通過手動連接,可以很容易的調試,可以很好的定位問題出現哪裏。

先概括下wifi手動連接的幾個步驟:
a:加載wifi驅動
b:給wifi上電(optional)
c:此時應該出來了網絡接口(譬如wlan0),給網絡接口上電(ifconfig wlan0 up)
d:對於支持sta/p2p共存的,這個時候可以通過iwpriv命令,生成p2p0網絡接口
e:設定wpa_supplicant服務所需的配置文件:wpa_supplicant.conf
f:啓動wpa_supplicant,在後臺運行
g:啓動wpa_cli,並且連接到wpa_supplicant,這個時候可以通過wpa_cli來發送命令給wpa_supplicant來執行。

譬如有時,通過android,有些ap連不上,或不能掃描,或是鑑權失敗,則這個時候可以試試通過手動操作來驗證問題。如果手動測試是ok的,說明問題是出現在android層的(wifi Setting, wifi framework, wifi service等層)


步驟a:加載wifi驅動

通過insmod命令加載wifi驅動模塊,由於mt5931在系統啓動階段的init.rc中,就已經被加載了。所以此處不需要手動加載。見下圖(高亮部分即爲mt5931所需的驅動模塊):



步驟b:由於mt5931的上電,是通過設備節點來控制的。如下:


步驟c:ifconfig wlan0 up
步驟d:通過iwpriv命令,生成了p2p0,通過ifconfig -a命令可以看到,此時已經有兩個網絡接口了。

步驟e:設置wpa_supplicant.conf文件,關鍵是設置ctrl_interface字段,因爲該字段直接設定了控制socket的服務器端的節點位置。另外通過配置該文件,還可以在起wpa_supplicant時,讓wifi直接連接上指定的AP.方法如下:

如上配置文件,是將wpa_supplicant的service端的socket節點設置在/data/misc/wpa_supplicant目錄下,該service socket節點將在wpa_supplicant啓動階段根據這個配置的指定就會建立;並且還指定了自動連接wpa2加密方式的ap:ASUS_BEN_NEW

步驟f:啓動wpa_supplicant,在後臺運行

以上啓動命令中,-i選項指定網絡接口,由於是sta/p2p共存,所有需要同時指定兩個網絡接口了。每個接口對應的配置文件通過-c選項來指定;-D選項,用來指定wext方式還是netlink的方式跟內核進行的交互;-dd選項則是設置打印信息的級別爲debug級;-N表示開始描述一個新的網絡接口;&表示後臺執行。

步驟g:啓動wpa_cli,其中-p選項,指定該客服端程序要連接到wpa_supplicant中的service端的socket節點的位置,即控制socket的路徑。該位置需要跟wpa_supplicant.conf文件中的設定要匹配


至此可以在wpa_cli的命令輸入提示符後面,輸入要測試的命令。還可以直接敲入help命令,可以得到wpa_cli所支持的命令。在這個下面,可以執行手動連接p2p,手動連接Ap等操作。
譬如測試pktcnt_poll命令:

以及手動執行掃描(scan),和獲取掃描結果的命令(scan_results):


由於客服端程序wpa_cli連接到服務器端的控制socket,是通過wpa_ctrl_open函數實現的,該函數展開如下:
struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
{
	struct wpa_ctrl *ctrl;
	static int counter = 0;
	int ret;
	size_t res;
	int tries = 0;
	int flags;

	ctrl = os_malloc(sizeof(*ctrl));
	if (ctrl == NULL)
		return NULL;
	os_memset(ctrl, 0, sizeof(*ctrl));

	ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0);
	if (ctrl->s < 0) {
		os_free(ctrl);
		return NULL;
	}

	ctrl->local.sun_family = AF_UNIX;
	counter++;
try_again:   //如下語句指定了客服端的socket節點是在/data/misc/wifi/sockets路徑下,並且節點名是有當前線程的ID和一個序號數組成。
	ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path),
			  CONFIG_CTRL_IFACE_CLIENT_DIR "/"
			  CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
			  (int) getpid(), counter);
	if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) {
		close(ctrl->s);
		os_free(ctrl);
		return NULL;
	}
	tries++;
	if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
		    sizeof(ctrl->local)) < 0) {//指定bind的時候,就會創建該client端的socket節點
		if (errno == EADDRINUSE && tries < 2) {
			/*
			 * getpid() returns unique identifier for this instance
			 * of wpa_ctrl, so the existing socket file must have
			 * been left by unclean termination of an earlier run.
			 * Remove the file and try again.
			 */
			unlink(ctrl->local.sun_path);
			goto try_again;
		}
		close(ctrl->s);
		os_free(ctrl);
		return NULL;
	}

#if defined(ANDROID) && !defined(PURE_LINUX)
	chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
	chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
	/*
	 * If the ctrl_path isn't an absolute pathname, assume that
	 * it's the name of a socket in the Android reserved namespace.
	 * Otherwise, it's a normal UNIX domain socket appearing in the
	 * filesystem.
	 */
	if (ctrl_path != NULL && *ctrl_path != '/') {
		char buf[21];
		os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path);
		if (socket_local_client_connect(
			    ctrl->s, buf,
			    ANDROID_SOCKET_NAMESPACE_RESERVED,
			    SOCK_DGRAM) < 0) {
			close(ctrl->s);
			unlink(ctrl->local.sun_path);
			os_free(ctrl);
			return NULL;
		}
		return ctrl;
	}
#endif /* ANDROID */

	ctrl->dest.sun_family = AF_UNIX;
	res = os_strlcpy(ctrl->dest.sun_path, ctrl_path,
			 sizeof(ctrl->dest.sun_path));
	if (res >= sizeof(ctrl->dest.sun_path)) {
		close(ctrl->s);
		os_free(ctrl);
		return NULL;
	}//開始發起連接到service端的socket節點
	if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
		    sizeof(ctrl->dest)) < 0) {
		close(ctrl->s);
		unlink(ctrl->local.sun_path);
		os_free(ctrl);
		return NULL;
	}

	/*
	 * Make socket non-blocking so that we don't hang forever if
	 * target dies unexpectedly.
	 */
	flags = fcntl(ctrl->s, F_GETFL);
	if (flags >= 0) {
		flags |= O_NONBLOCK;
		if (fcntl(ctrl->s, F_SETFL, flags) < 0) {
			perror("fcntl(ctrl->s, O_NONBLOCK)");
			/* Not fatal, continue on.*/
		}
	}

	return ctrl;
}
如下你可以看到wpa_cli進程號是1517,而/data/misc/wifi/sockets下的client端socket節點的名字就是以該進程ID和序號組成的。兩個節點,一個用來傳輸控制命令,一個用來接受wpa_supplicant報上來的事件。

再來看下services段的socket節點,如上wpa_supplicant.conf文件所指定的,service端的控制socket節點,是在如下路徑的:

service端的socket節點名字就是各自的網絡接口名字。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章