深入理解 wpa_supplicant(二)

本文为《深入理解Android Wi-Fi、NFC和GPS卷》读书笔记,Android源码为Android 5.1

struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
{
	struct wpa_global *global;
	int ret, i;

	if (params == NULL)
		return NULL;

#ifdef CONFIG_DRIVER_NDIS
	{
		void driver_ndis_init_ops(void);
		driver_ndis_init_ops();
	}
#endif /* CONFIG_DRIVER_NDIS */

#ifndef CONFIG_NO_WPA_MSG
	//设置全局回调函数
	wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
#endif /* CONFIG_NO_WPA_MSG */

	//输出日志文件设置
	wpa_debug_open_file(params->wpa_debug_file_path);
	if (params->wpa_debug_syslog)
		wpa_debug_open_syslog();
	if (params->wpa_debug_tracing) {
		ret = wpa_debug_open_linux_tracing();
		if (ret) {
			wpa_printf(MSG_ERROR,
				   "Failed to enable trace logging");
			return NULL;
		}
	}

	ret = eap_register_methods();	//注册 EAP 方法
	if (ret) {
		wpa_printf(MSG_ERROR, "Failed to register EAP methods");
		if (ret == -2)
			wpa_printf(MSG_ERROR, "Two or more EAP methods used "
				   "the same EAP type.");
		return NULL;
	}

	global = os_zalloc(sizeof(*global));	//创建一个 wpa_global 对象,后面是初始化
	if (global == NULL)
		return NULL;
	dl_list_init(&global->p2p_srv_bonjour);
	dl_list_init(&global->p2p_srv_upnp);
	global->params.daemonize = params->daemonize;
	global->params.wait_for_monitor = params->wait_for_monitor;
	global->params.dbus_ctrl_interface = params->dbus_ctrl_interface;
	if (params->pid_file)
		global->params.pid_file = os_strdup(params->pid_file);
	if (params->ctrl_interface)
		global->params.ctrl_interface =
			os_strdup(params->ctrl_interface);
	if (params->override_driver)
		global->params.override_driver =
			os_strdup(params->override_driver);
	if (params->override_ctrl_interface)
		global->params.override_ctrl_interface =
			os_strdup(params->override_ctrl_interface);
	wpa_debug_level = global->params.wpa_debug_level =
		params->wpa_debug_level;
	wpa_debug_show_keys = global->params.wpa_debug_show_keys =
		params->wpa_debug_show_keys;
	wpa_debug_timestamp = global->params.wpa_debug_timestamp =
		params->wpa_debug_timestamp;

	wpa_printf(MSG_DEBUG, "wpa_supplicant v" VERSION_STR);

	if (eloop_init()) {
		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
		wpa_supplicant_deinit(global);
		return NULL;
	}

	//初始化随机数相关资源,用于提升后续随机数生成的随机性
	random_init(params->entropy_file);

	//初始化全局控制接口对象
	global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global);
	if (global->ctrl_iface == NULL) {
		wpa_supplicant_deinit(global);
		return NULL;
	}

	//初始化通知机制相关资源
	if (wpas_notify_supplicant_initialized(global)) {
		wpa_supplicant_deinit(global);
		return NULL;
	}

	//wpa_drivers 是一个全局变量
	for (i = 0; wpa_drivers[i]; i++)
		global->drv_count++;
	if (global->drv_count == 0) {
		wpa_printf(MSG_ERROR, "No drivers enabled");
		wpa_supplicant_deinit(global);
		return NULL;
	}
	//分配全局 driver wrapper 上下文信息数组
	global->drv_priv = os_zalloc(global->drv_count * sizeof(void *));
	if (global->drv_priv == NULL) {
		wpa_supplicant_deinit(global);
		return NULL;
	}

#ifdef CONFIG_WIFI_DISPLAY
	if (wifi_display_init(global) < 0) {
		wpa_printf(MSG_ERROR, "Failed to initialize Wi-Fi Display");
		wpa_supplicant_deinit(global);
		return NULL;
	}
#endif /* CONFIG_WIFI_DISPLAY */

	return global;
}
wpa_supplicant_init 函数的主要功能是初始化 wpa_global 以及一些与整个程序相关的资源,包括随机数资源、 eloop 时间循环机制以及设置消息全局回调函数。
wpa_msg_get_ifname_func: 获取网卡接口名
wpa_msg_cb_func: 通过该回调函数进行一些特殊处理
android-5.1/external/wpa_supplicant_8/wpa_supplicant/src/utils/wpa_debug.c

static wpa_msg_cb_func wpa_msg_cb = NULL;

void wpa_msg_register_cb(wpa_msg_cb_func func)
{
	wpa_msg_cb = func;
}


static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL;

void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func)
{
	wpa_msg_ifname_cb = func;
}
该文件只有这一个函数,该函数主要根据编译时的配置项来初始化不同的 eap 方法
android-5.1/external/wpa_supplicant_8/wpa_supplicant/eap_register.c

int eap_register_methods(void)
{
	int ret = 0;

#ifdef EAP_MD5
	if (ret == 0)
		ret = eap_peer_md5_register();
#endif /* EAP_MD5 */

#ifdef EAP_TLS
	if (ret == 0)
		ret = eap_peer_tls_register();
#endif /* EAP_TLS */

#ifdef EAP_UNAUTH_TLS
	if (ret == 0)
		ret = eap_peer_unauth_tls_register();
#endif /* EAP_UNAUTH_TLS */

#ifdef EAP_MSCHAPv2
	if (ret == 0)
		ret = eap_peer_mschapv2_register();
#endif /* EAP_MSCHAPv2 */

#ifdef EAP_PEAP
	if (ret == 0)
		ret = eap_peer_peap_register();
#endif /* EAP_PEAP */

#ifdef EAP_TTLS
	if (ret == 0)
		ret = eap_peer_ttls_register();
#endif /* EAP_TTLS */

#ifdef EAP_GTC
	if (ret == 0)
		ret = eap_peer_gtc_register();
#endif /* EAP_GTC */

#ifdef EAP_OTP
	if (ret == 0)
		ret = eap_peer_otp_register();
#endif /* EAP_OTP */

#ifdef EAP_SIM
	if (ret == 0)
		ret = eap_peer_sim_register();
#endif /* EAP_SIM */

#ifdef EAP_LEAP
	if (ret == 0)
		ret = eap_peer_leap_register();
#endif /* EAP_LEAP */

#ifdef EAP_PSK
	if (ret == 0)
		ret = eap_peer_psk_register();
#endif /* EAP_PSK */

#ifdef EAP_AKA
	if (ret == 0)
		ret = eap_peer_aka_register();
#endif /* EAP_AKA */

#ifdef EAP_AKA_PRIME
	if (ret == 0)
		ret = eap_peer_aka_prime_register();
#endif /* EAP_AKA_PRIME */

#ifdef EAP_FAST
	if (ret == 0)
		ret = eap_peer_fast_register();
#endif /* EAP_FAST */

#ifdef EAP_PAX
	if (ret == 0)
		ret = eap_peer_pax_register();
#endif /* EAP_PAX */

#ifdef EAP_SAKE
	if (ret == 0)
		ret = eap_peer_sake_register();
#endif /* EAP_SAKE */

#ifdef EAP_GPSK
	if (ret == 0)
		ret = eap_peer_gpsk_register();
#endif /* EAP_GPSK */

#ifdef EAP_WSC
	if (ret == 0)
		ret = eap_peer_wsc_register();
#endif /* EAP_WSC */

#ifdef EAP_IKEV2
	if (ret == 0)
		ret = eap_peer_ikev2_register();
#endif /* EAP_IKEV2 */

#ifdef EAP_VENDOR_TEST
	if (ret == 0)
		ret = eap_peer_vendor_test_register();
#endif /* EAP_VENDOR_TEST */

#ifdef EAP_TNC
	if (ret == 0)
		ret = eap_peer_tnc_register();
#endif /* EAP_TNC */

#ifdef EAP_PWD
	if (ret == 0)
		ret = eap_peer_pwd_register();
#endif /* EAP_PWD */

#ifdef EAP_SERVER_IDENTITY
	if (ret == 0)
		ret = eap_server_identity_register();
#endif /* EAP_SERVER_IDENTITY */

#ifdef EAP_SERVER_MD5
	if (ret == 0)
		ret = eap_server_md5_register();
#endif /* EAP_SERVER_MD5 */

#ifdef EAP_SERVER_TLS
	if (ret == 0)
		ret = eap_server_tls_register();
#endif /* EAP_SERVER_TLS */

#ifdef EAP_SERVER_UNAUTH_TLS
	if (ret == 0)
		ret = eap_server_unauth_tls_register();
#endif /* EAP_SERVER_UNAUTH_TLS */

#ifdef EAP_SERVER_MSCHAPV2
	if (ret == 0)
		ret = eap_server_mschapv2_register();
#endif /* EAP_SERVER_MSCHAPV2 */

#ifdef EAP_SERVER_PEAP
	if (ret == 0)
		ret = eap_server_peap_register();
#endif /* EAP_SERVER_PEAP */

#ifdef EAP_SERVER_TLV
	if (ret == 0)
		ret = eap_server_tlv_register();
#endif /* EAP_SERVER_TLV */

#ifdef EAP_SERVER_GTC
	if (ret == 0)
		ret = eap_server_gtc_register();
#endif /* EAP_SERVER_GTC */

#ifdef EAP_SERVER_TTLS
	if (ret == 0)
		ret = eap_server_ttls_register();
#endif /* EAP_SERVER_TTLS */

#ifdef EAP_SERVER_SIM
	if (ret == 0)
		ret = eap_server_sim_register();
#endif /* EAP_SERVER_SIM */

#ifdef EAP_SERVER_AKA
	if (ret == 0)
		ret = eap_server_aka_register();
#endif /* EAP_SERVER_AKA */

#ifdef EAP_SERVER_AKA_PRIME
	if (ret == 0)
		ret = eap_server_aka_prime_register();
#endif /* EAP_SERVER_AKA_PRIME */

#ifdef EAP_SERVER_PAX
	if (ret == 0)
		ret = eap_server_pax_register();
#endif /* EAP_SERVER_PAX */

#ifdef EAP_SERVER_PSK
	if (ret == 0)
		ret = eap_server_psk_register();
#endif /* EAP_SERVER_PSK */

#ifdef EAP_SERVER_SAKE
	if (ret == 0)
		ret = eap_server_sake_register();
#endif /* EAP_SERVER_SAKE */

#ifdef EAP_SERVER_GPSK
	if (ret == 0)
		ret = eap_server_gpsk_register();
#endif /* EAP_SERVER_GPSK */

#ifdef EAP_SERVER_VENDOR_TEST
	if (ret == 0)
		ret = eap_server_vendor_test_register();
#endif /* EAP_SERVER_VENDOR_TEST */

#ifdef EAP_SERVER_FAST
	if (ret == 0)
		ret = eap_server_fast_register();
#endif /* EAP_SERVER_FAST */

#ifdef EAP_SERVER_WSC
	if (ret == 0)
		ret = eap_server_wsc_register();
#endif /* EAP_SERVER_WSC */

#ifdef EAP_SERVER_IKEV2
	if (ret == 0)
		ret = eap_server_ikev2_register();
#endif /* EAP_SERVER_IKEV2 */

#ifdef EAP_SERVER_TNC
	if (ret == 0)
		ret = eap_server_tnc_register();
#endif /* EAP_SERVER_TNC */

#ifdef EAP_SERVER_PWD
	if (ret == 0)
		ret = eap_server_pwd_register();
#endif /* EAP_SERVER_PWD */

	return ret;
}

eap_register_methods 将根据编译配置项来注册所需的 eap method。 例如 MD5 身份验证方法对应的注册函数是 eap_peer_md5_register,该函数内部将填充一个名为 eap_method 的数据结构。

struct eap_method 结构体内部一些变量及函数指针的定义和 RFC4137 有较大关系。
android-5.1/external/wpa_supplicant_8/src/eap_peer/eap_i.h

struct eap_method {
	/**
	 * vendor - EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
	 */
	int vendor;	//EAP方法的厂商ID

	/**
	 * method - EAP type number (EAP_TYPE_*)
	 */
	EapType method;	//EAP方法枚举定义

	/**
	 * name - Name of the method (e.g., "TLS")
	 */
	const char *name;	//EAP 方法名

	/**
	 * init - Initialize an EAP method
	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
	 * Returns: Pointer to allocated private data, or %NULL on failure
	 *
	 * This function is used to initialize the EAP method explicitly
	 * instead of using METHOD_INIT state as specific in RFC 4137. The
	 * method is expected to initialize it method-specific state and return
	 * a pointer that will be used as the priv argument to other calls.
	 */
	void * (*init)(struct eap_sm *sm);	//该 eap_method 对象的初始化函数

	/**
	 * deinit - Deinitialize an EAP method
	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
	 * @priv: Pointer to private EAP method data from eap_method::init()
	 *
	 * Deinitialize the EAP method and free any allocated private data.
	 */
	void (*deinit)(struct eap_sm *sm, void *priv);	//资源释放函数

	/**
	 * process - Process an EAP request
	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
	 * @priv: Pointer to private EAP method data from eap_method::init()
	 * @ret: Return values from EAP request validation and processing
	 * @reqData: EAP request to be processed (eapReqData)
	 * Returns: Pointer to allocated EAP response packet (eapRespData)
	 *
	 * This function is a combination of m.check(), m.process(), and
	 * m.buildResp() procedures defined in section 4.4 of RFC 4137 In other
	 * words, this function validates the incoming request, processes it,
	 * and build a response packet. m.check() and m.process() return values
	 * are returned through struct eap_method_ret *ret variable. Caller is
	 * responsible for freeing the returned EAP response packet.
	 */
	struct wpabuf * (*process)(struct eap_sm *sm, void *priv,
				   struct eap_method_ret *ret,
				   const struct wpabuf *reqData);	//EAP request消息的处理函数

	/**
	 * isKeyAvailable - Find out whether EAP method has keying material
	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
	 * @priv: Pointer to private EAP method data from eap_method::init()
	 * Returns: %TRUE if key material (eapKeyData) is available
	 */
	Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv);

	/**
	 * getKey - Get EAP method specific keying material (eapKeyData)
	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
	 * @priv: Pointer to private EAP method data from eap_method::init()
	 * @len: Pointer to variable to store key length (eapKeyDataLen)
	 * Returns: Keying material (eapKeyData) or %NULL if not available
	 *
	 * This function can be used to get the keying material from the EAP
	 * method. The key may already be stored in the method-specific private
	 * data or this function may derive the key.
	 */
	u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);

	/**
	 * get_status - Get EAP method status
	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
	 * @priv: Pointer to private EAP method data from eap_method::init()
	 * @buf: Buffer for status information
	 * @buflen: Maximum buffer length
	 * @verbose: Whether to include verbose status information
	 * Returns: Number of bytes written to buf
	 *
	 * Query EAP method for status information. This function fills in a
	 * text area with current status information from the EAP method. If
	 * the buffer (buf) is not large enough, status information will be
	 * truncated to fit the buffer.
	 */
	int (*get_status)(struct eap_sm *sm, void *priv, char *buf,
			  size_t buflen, int verbose);

	/**
	 * has_reauth_data - Whether method is ready for fast reauthentication
	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
	 * @priv: Pointer to private EAP method data from eap_method::init()
	 * Returns: %TRUE or %FALSE based on whether fast reauthentication is
	 * possible
	 *
	 * This function is an optional handler that only EAP methods
	 * supporting fast re-authentication need to implement.
	 */
	Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv);

	/**
	 * deinit_for_reauth - Release data that is not needed for fast re-auth
	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
	 * @priv: Pointer to private EAP method data from eap_method::init()
	 *
	 * This function is an optional handler that only EAP methods
	 * supporting fast re-authentication need to implement. This is called
	 * when authentication has been completed and EAP state machine is
	 * requesting that enough state information is maintained for fast
	 * re-authentication
	 */
	void (*deinit_for_reauth)(struct eap_sm *sm, void *priv);

	/**
	 * init_for_reauth - Prepare for start of fast re-authentication
	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
	 * @priv: Pointer to private EAP method data from eap_method::init()
	 *
	 * This function is an optional handler that only EAP methods
	 * supporting fast re-authentication need to implement. This is called
	 * when EAP authentication is started and EAP state machine is
	 * requesting fast re-authentication to be used.
	 */
	void * (*init_for_reauth)(struct eap_sm *sm, void *priv);

	/**
	 * get_identity - Get method specific identity for re-authentication
	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
	 * @priv: Pointer to private EAP method data from eap_method::init()
	 * @len: Length of the returned identity
	 * Returns: Pointer to the method specific identity or %NULL if default
	 * identity is to be used
	 *
	 * This function is an optional handler that only EAP methods
	 * that use method specific identity need to implement.
	 */
	const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);

	/**
	 * free - Free EAP method data
	 * @method: Pointer to the method data registered with
	 * eap_peer_method_register().
	 *
	 * This function will be called when the EAP method is being
	 * unregistered. If the EAP method allocated resources during
	 * registration (e.g., allocated struct eap_method), they should be
	 * freed in this function. No other method functions will be called
	 * after this call. If this function is not defined (i.e., function
	 * pointer is %NULL), a default handler is used to release the method
	 * data with free(method). This is suitable for most cases.
	 */
	void (*free)(struct eap_method *method);	//释放 eap_method 对象内部资源

#define EAP_PEER_METHOD_INTERFACE_VERSION 1
	/**
	 * version - Version of the EAP peer method interface
	 *
	 * The EAP peer method implementation should set this variable to
	 * EAP_PEER_METHOD_INTERFACE_VERSION. This is used to verify that the
	 * EAP method is using supported API version when using dynamically
	 * loadable EAP methods.
	 */
	int version;	//EAP版本号

	/**
	 * next - Pointer to the next EAP method
	 *
	 * This variable is used internally in the EAP method registration code
	 * to create a linked list of registered EAP methods.
	 */
	struct eap_method *next;	//所有注册的 eap_method 对象都存储在一个单向链表中

#ifdef CONFIG_DYNAMIC_EAP_METHODS
	/**
	 * dl_handle - Handle for the dynamic library
	 *
	 * This variable is used internally in the EAP method registration code
	 * to store a handle for the dynamic library. If the method is linked
	 * in statically, this is %NULL.
	 */
	void *dl_handle;
#endif /* CONFIG_DYNAMIC_EAP_METHODS */

	/**
	 * get_emsk - Get EAP method specific keying extended material (EMSK)
	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
	 * @priv: Pointer to private EAP method data from eap_method::init()
	 * @len: Pointer to a variable to store EMSK length
	 * Returns: EMSK or %NULL if not available
	 *
	 * This function can be used to get the extended keying material from
	 * the EAP method. The key may already be stored in the method-specific
	 * private data or this function may derive the key.
	 */
	u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);

	/**
	 * getSessionId - Get EAP method specific Session-Id
	 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
	 * @priv: Pointer to private EAP method data from eap_method::init()
	 * @len: Pointer to a variable to store Session-Id length
	 * Returns: Session-Id or %NULL if not available
	 *
	 * This function can be used to get the Session-Id from the EAP method.
	 * The Session-Id may already be stored in the method-specific private
	 * data or this function may derive the Session-Id.
	 */
	u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len);
};
android-5.1/external/wpa_supplicant_8/src/utils/eloop.c

int eloop_init(void)
{
	os_memset(&eloop, 0, sizeof(eloop));
	dl_list_init(&eloop.timeout);
#ifdef CONFIG_ELOOP_EPOLL
	eloop.epollfd = epoll_create1(0);
	if (eloop.epollfd < 0) {
		wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s\n",
			   __func__, strerror(errno));
		return -1;
	}
	eloop.readers.type = EVENT_TYPE_READ;
	eloop.writers.type = EVENT_TYPE_WRITE;
	eloop.exceptions.type = EVENT_TYPE_EXCEPTION;
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef WPA_TRACE
	signal(SIGSEGV, eloop_sigsegv_handler);
#endif /* WPA_TRACE */
	return 0;
}
eloop_init 初始化了 WPAS 中事件驱动的核心数据结构体 eloop_data。 WPAS 事件驱动机制就是利用 epoll(如果编译时设置了 CONFIG_ELOOP_POOL 选项)或 select 实现了 I/O 复用。
android-5.1/external/wpa_supplicant_8/src/utils/eloop.c

struct eloop_data {
	int max_sock;	//供select使用

	int count; /* sum of all table counts 所有事件表中事件的个数*/
#ifdef CONFIG_ELOOP_POLL
	int max_pollfd_map; /* number of pollfds_map currently allocated */
	int max_poll_fds; /* number of pollfds currently allocated */
	struct pollfd *pollfds;
	struct pollfd **pollfds_map;
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_EPOLL
	int epollfd;
	int epoll_max_event_num;
	int epoll_max_fd;
	struct eloop_sock *epoll_table;
	struct epoll_event *epoll_events;
#endif /* CONFIG_ELOOP_EPOLL */
	struct eloop_sock_table readers;	//读事件表
	struct eloop_sock_table writers;	//写事件表
	struct eloop_sock_table exceptions;	//异常事件表

	struct dl_list timeout;	//超时事件链表

	int signal_count;	//信号事件个数
	struct eloop_signal *signals; //信号事件表
	int signaled;
	int pending_terminate;

	int terminate;
};
从事件角度来看, WPAS 的事件驱动机制支持5中类型的event。
read event: 读事件,例如来自 socket 的可读时间
write event:写事件,例如 socket 的可写事件
exception event: 异常事件, 如果socket 操作发生错误,则由错误事件处理
timeout event:定时事件,通过 select 的等待超时机制来实现定时事件
signal: 信号事件,信号事件来源于 Kernel。 WPAS 允许为一些特定信号设置处理函数
以上这些事件相关的信息都保存在 eloop_data 结构体中。
android-5.1/external/wpa_supplicant_8/src/utils/eloop.h

/**
 * eloop_register_read_sock - Register handler for read events
 * @sock: File descriptor number for the socket
 * @handler: Callback function to be called when data is available for reading
 * @eloop_data: Callback context data (eloop_ctx)
 * @user_data: Callback context data (sock_ctx)
 * Returns: 0 on success, -1 on failure
 *
 * Register a read socket notifier for the given file descriptor. The handler
 * function will be called whenever data is available for reading from the
 * socket. The handler function is responsible for clearing the event after
 * having processed it in order to avoid eloop from calling the handler again
 * for the same event.
 */
 //注册 socket 读事件处理函数,参数sock代表一个socket句柄。一旦该句柄上有读事件发生,则 handler 函数将被事件处理循环调用
int eloop_register_read_sock(int sock, eloop_sock_handler handler,
			     void *eloop_data, void *user_data);
/**
 * eloop_register_sock - Register handler for socket events
 * @sock: File descriptor number for the socket
 * @type: Type of event to wait for
 * @handler: Callback function to be called when the event is triggered
 * @eloop_data: Callback context data (eloop_ctx)
 * @user_data: Callback context data (sock_ctx)
 * Returns: 0 on success, -1 on failure
 *
 * Register an event notifier for the given socket's file descriptor. The
 * handler function will be called whenever the that event is triggered for the
 * socket. The handler function is responsible for clearing the event after
 * having processed it in order to avoid eloop from calling the handler again
 * for the same event.
 */
 //注册 socket 事件处理函数,具体是哪种事件(只能是读、写或异常)由type参数决定
int eloop_register_sock(int sock, eloop_event_type type,
			eloop_sock_handler handler,
			void *eloop_data, void *user_data);
/**
 * eloop_register_timeout - Register timeout
 * @secs: Number of seconds to the timeout
 * @usecs: Number of microseconds to the timeout
 * @handler: Callback function to be called when timeout occurs
 * @eloop_data: Callback context data (eloop_ctx)
 * @user_data: Callback context data (sock_ctx)
 * Returns: 0 on success, -1 on failure
 *
 * Register a timeout that will cause the handler function to be called after
 * given time.
 */
 //注册超时事件处理函数
int eloop_register_timeout(unsigned int secs, unsigned int usecs,
			   eloop_timeout_handler handler,
			   void *eloop_data, void *user_data);
/**
 * eloop_register_signal - Register handler for signals
 * @sig: Signal number (e.g., SIGHUP)
 * @handler: Callback function to be called when the signal is received
 * @user_data: Callback context data (signal_ctx)
 * Returns: 0 on success, -1 on failure
 *
 * Register a callback function that will be called when a signal is received.
 * The callback function is actually called only after the system signal
 * handler has returned. This means that the normal limits for sighandlers
 * (i.e., only "safe functions" allowed) do not apply for the registered
 * callback.
 */
 //注册信号事件处理函数,具体要处理的信号由 sig 参数指定
int eloop_register_signal(int sig, eloop_signal_handler handler,
			  void *user_data);

WPAS 事件驱动机制的运行原理:
android-5.1/external/wpa_supplicant_8/src/utils/eloop.c

void eloop_run(void)
{
#ifdef CONFIG_ELOOP_POLL
    int num_poll_fds;
    int timeout_ms = 0;
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECT
    fd_set *rfds, *wfds, *efds;    //fd_set 是 select 中用到的一种参数类型
    struct timeval _tv;
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
    int timeout_ms = -1;
#endif /* CONFIG_ELOOP_EPOLL */
    int res;
    struct os_reltime tv, now;

#ifdef CONFIG_ELOOP_SELECT
    rfds = os_malloc(sizeof(*rfds));
    wfds = os_malloc(sizeof(*wfds));
    efds = os_malloc(sizeof(*efds));
    if (rfds == NULL || wfds == NULL || efds == NULL)
        goto out;
#endif /* CONFIG_ELOOP_SELECT */

    //事件驱动循环
    while (!eloop.terminate &&
           (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
        eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
        struct eloop_timeout *timeout;
        //判断是否有超时事件需要等待
        timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
                    list);
        if (timeout) {
            os_get_reltime(&now);
            if (os_reltime_before(&now, &timeout->time))
                os_reltime_sub(&timeout->time, &now, &tv);
            else
                tv.sec = tv.usec = 0;
#if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
            timeout_ms = tv.sec * 1000 + tv.usec / 1000;
#endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
#ifdef CONFIG_ELOOP_SELECT
            _tv.tv_sec = tv.sec;
            _tv.tv_usec = tv.usec;
#endif /* CONFIG_ELOOP_SELECT */
        }

#ifdef CONFIG_ELOOP_POLL
        num_poll_fds = eloop_sock_table_set_fds(
            &eloop.readers, &eloop.writers, &eloop.exceptions,
            eloop.pollfds, eloop.pollfds_map,
            eloop.max_pollfd_map);
        res = poll(eloop.pollfds, num_poll_fds,
               timeout ? timeout_ms : -1);
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECT
        //将外界设置的读、写、异常事件添加到对应的 fd_set 中
        eloop_sock_table_set_fds(&eloop.readers, rfds);
        eloop_sock_table_set_fds(&eloop.writers, wfds);
        eloop_sock_table_set_fds(&eloop.exceptions, efds);
        //调用select函数
        res = select(eloop.max_sock + 1, rfds, wfds, efds,
                 timeout ? &_tv : NULL);
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
        if (eloop.count == 0) {
            res = 0;
        } else {
            res = epoll_wait(eloop.epollfd, eloop.epoll_events,
                     eloop.count, timeout_ms);
        }
#endif /* CONFIG_ELOOP_EPOLL */
        if (res < 0 && errno != EINTR && errno != 0) {
            wpa_printf(MSG_ERROR, "eloop: %s: %s",
#ifdef CONFIG_ELOOP_POLL
                   "poll"
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECT
                   "select"
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
                   "epoll"
#endif /* CONFIG_ELOOP_EPOLL */
                   , strerror(errno));
            goto out;
        }
        //先处理信号事件
        eloop_process_pending_signals();

        /* check if some registered timeouts have occurred */
        //判断是否有超时事件发生
        timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
                    list);
        if (timeout) {
            os_get_reltime(&now);
            if (!os_reltime_before(&now, &timeout->time)) {
                void *eloop_data = timeout->eloop_data;
                void *user_data = timeout->user_data;
                eloop_timeout_handler handler =
                    timeout->handler;
                eloop_remove_timeout(timeout);    //超时事件只执行一次
                handler(eloop_data, user_data);    //处理超时事件
            }

        }

        if (res <= 0)
            continue;

#ifdef CONFIG_ELOOP_POLL
        eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
                      &eloop.exceptions, eloop.pollfds_map,
                      eloop.max_pollfd_map);
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECT
        //处理读、写、异常事件
        eloop_sock_table_dispatch(&eloop.readers, rfds);
        eloop_sock_table_dispatch(&eloop.writers, wfds);
        eloop_sock_table_dispatch(&eloop.exceptions, efds);
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
        eloop_sock_table_dispatch(eloop.epoll_events, res);
#endif /* CONFIG_ELOOP_EPOLL */
    }

    eloop.terminate = 0;
out:
#ifdef CONFIG_ELOOP_SELECT
    os_free(rfds);
    os_free(wfds);
    os_free(efds);
#endif /* CONFIG_ELOOP_SELECT */
    return;
}
wpa_drivers 是一个全局数组变量 android-5.1/external/wpa_supplicant_8/src/drivers/drivers.c

struct wpa_driver_ops *wpa_drivers[] =
{
#ifdef CONFIG_DRIVER_NL80211
	&wpa_driver_nl80211_ops,
#endif /* CONFIG_DRIVER_NL80211 */
#ifdef CONFIG_DRIVER_WEXT
	&wpa_driver_wext_ops,
#endif /* CONFIG_DRIVER_WEXT */
#ifdef CONFIG_DRIVER_HOSTAP
	&wpa_driver_hostap_ops,
#endif /* CONFIG_DRIVER_HOSTAP */
#ifdef CONFIG_DRIVER_MADWIFI
	&wpa_driver_madwifi_ops,
#endif /* CONFIG_DRIVER_MADWIFI */
#ifdef CONFIG_DRIVER_BSD
	&wpa_driver_bsd_ops,
#endif /* CONFIG_DRIVER_BSD */
#ifdef CONFIG_DRIVER_OPENBSD
	&wpa_driver_openbsd_ops,
#endif /* CONFIG_DRIVER_OPENBSD */
#ifdef CONFIG_DRIVER_NDIS
	&wpa_driver_ndis_ops,
#endif /* CONFIG_DRIVER_NDIS */
#ifdef CONFIG_DRIVER_WIRED
	&wpa_driver_wired_ops,
#endif /* CONFIG_DRIVER_WIRED */
#ifdef CONFIG_DRIVER_MACSEC_QCA
	&wpa_driver_macsec_qca_ops,
#endif /* CONFIG_DRIVER_MACSEC_QCA */
#ifdef CONFIG_DRIVER_TEST
	&wpa_driver_test_ops,
#endif /* CONFIG_DRIVER_TEST */
#ifdef CONFIG_DRIVER_ROBOSWITCH
	&wpa_driver_roboswitch_ops,
#endif /* CONFIG_DRIVER_ROBOSWITCH */
#ifdef CONFIG_DRIVER_ATHEROS
	&wpa_driver_atheros_ops,
#endif /* CONFIG_DRIVER_ATHEROS */
#ifdef CONFIG_DRIVER_NONE
	&wpa_driver_none_ops,
#endif /* CONFIG_DRIVER_NONE */
	NULL
};
wpa_driver_ops内部定义很多函数指针,通过这些定义的函数指针,WPAS能隔离上层使用者和具体的driver。
android-5.1/external/wpa_supplicant_8/src/drivers/driver_nl80211.c

const struct wpa_driver_ops wpa_driver_nl80211_ops = {
	.name = "nl80211",		//driver wrapper的名称
	.desc = "Linux nl80211/cfg80211",	//描述信息
	.get_bssid = wpa_driver_nl80211_get_bssid,	//用于获取 bssid
	.get_ssid = wpa_driver_nl80211_get_ssid,
	.set_key = driver_nl80211_set_key,
	.scan2 = driver_nl80211_scan2,	//扫描函数
	.sched_scan = wpa_driver_nl80211_sched_scan,
	.stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,
	.get_scan_results2 = wpa_driver_nl80211_get_scan_results,	//获取扫描结果
	.deauthenticate = driver_nl80211_deauthenticate,
	.authenticate = driver_nl80211_authenticate,	//触发 authentication 操作
	.associate = wpa_driver_nl80211_associate,	//触发 association 操作
	.global_init = nl80211_global_init,	//driver wrapper 全局初始化函数,该函数的返回值保存在 wpa_global 成员变量 drv_pri 数组中
	.global_deinit = nl80211_global_deinit,
	.init2 = wpa_driver_nl80211_init,	//driver wrapper 初始化函数
	.deinit = driver_nl80211_deinit,
	.get_capa = wpa_driver_nl80211_get_capa,
	.set_operstate = wpa_driver_nl80211_set_operstate,
	.set_supp_port = wpa_driver_nl80211_set_supp_port,
	.set_country = wpa_driver_nl80211_set_country,
	.get_country = wpa_driver_nl80211_get_country,
	.set_ap = wpa_driver_nl80211_set_ap,
	.set_acl = wpa_driver_nl80211_set_acl,
	.if_add = wpa_driver_nl80211_if_add,
	.if_remove = driver_nl80211_if_remove,
	.send_mlme = driver_nl80211_send_mlme,
	.get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data,
	.sta_add = wpa_driver_nl80211_sta_add,
	.sta_remove = driver_nl80211_sta_remove,
	.hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
	.sta_set_flags = wpa_driver_nl80211_sta_set_flags,
	.hapd_init = i802_init,
	.hapd_deinit = i802_deinit,
	.set_wds_sta = i802_set_wds_sta,
	.get_seqnum = i802_get_seqnum,
	.flush = i802_flush,
	.get_inact_sec = i802_get_inact_sec,
	.sta_clear_stats = i802_sta_clear_stats,
	.set_rts = i802_set_rts,
	.set_frag = i802_set_frag,
	.set_tx_queue_params = i802_set_tx_queue_params,
	.set_sta_vlan = driver_nl80211_set_sta_vlan,
	.sta_deauth = i802_sta_deauth,
	.sta_disassoc = i802_sta_disassoc,
	.read_sta_data = driver_nl80211_read_sta_data,
	.set_freq = i802_set_freq,
	.send_action = driver_nl80211_send_action,
	.send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait,
	.remain_on_channel = wpa_driver_nl80211_remain_on_channel,
	.cancel_remain_on_channel =	wpa_driver_nl80211_cancel_remain_on_channel,
	.probe_req_report = driver_nl80211_probe_req_report,
	.deinit_ap = wpa_driver_nl80211_deinit_ap,
	.deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli,
	.resume = wpa_driver_nl80211_resume,
	.send_ft_action = nl80211_send_ft_action,
	.signal_monitor = nl80211_signal_monitor,
	.signal_poll = nl80211_signal_poll,
	.send_frame = nl80211_send_frame,
	.shared_freq = wpa_driver_nl80211_shared_freq,
	.set_param = nl80211_set_param,
	.get_radio_name = nl80211_get_radio_name,
	.add_pmkid = nl80211_add_pmkid,
	.remove_pmkid = nl80211_remove_pmkid,
	.flush_pmkid = nl80211_flush_pmkid,
	.set_rekey_info = nl80211_set_rekey_info,
	.poll_client = nl80211_poll_client,
	.set_p2p_powersave = nl80211_set_p2p_powersave,
	.start_dfs_cac = nl80211_start_radar_detection,
	.stop_ap = wpa_driver_nl80211_stop_ap,
#ifdef CONFIG_TDLS
	.send_tdls_mgmt = nl80211_send_tdls_mgmt,
	.tdls_oper = nl80211_tdls_oper,
#endif /* CONFIG_TDLS */
	.update_ft_ies = wpa_driver_nl80211_update_ft_ies,
	.get_mac_addr = wpa_driver_nl80211_get_macaddr,
	.get_survey = wpa_driver_nl80211_get_survey,
	.status = wpa_driver_nl80211_status,
	.switch_channel = nl80211_switch_channel,
#ifdef ANDROID_P2P
	.set_noa = wpa_driver_set_p2p_noa,
	.get_noa = wpa_driver_get_p2p_noa,
	.set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie,
#endif /* ANDROID_P2P */
#ifdef ANDROID
	.driver_cmd = wpa_driver_nl80211_driver_cmd,	//用于处理和具体驱动相关的命令
#endif /* ANDROID */
	.vendor_cmd = nl80211_vendor_cmd,
	.set_qos_map = nl80211_set_qos_map,
	.set_wowlan = nl80211_set_wowlan,
	.roaming = nl80211_roaming,
	.set_mac_addr = nl80211_set_mac_addr,
};

你可能感兴趣的:(wifi)