本文为《深入理解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_DISPLAYif (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_MD5if (ret == 0)ret = eap_peer_md5_register();
#endif /* EAP_MD5 */#ifdef EAP_TLSif (ret == 0)ret = eap_peer_tls_register();
#endif /* EAP_TLS */#ifdef EAP_UNAUTH_TLSif (ret == 0)ret = eap_peer_unauth_tls_register();
#endif /* EAP_UNAUTH_TLS */#ifdef EAP_MSCHAPv2if (ret == 0)ret = eap_peer_mschapv2_register();
#endif /* EAP_MSCHAPv2 */#ifdef EAP_PEAPif (ret == 0)ret = eap_peer_peap_register();
#endif /* EAP_PEAP */#ifdef EAP_TTLSif (ret == 0)ret = eap_peer_ttls_register();
#endif /* EAP_TTLS */#ifdef EAP_GTCif (ret == 0)ret = eap_peer_gtc_register();
#endif /* EAP_GTC */#ifdef EAP_OTPif (ret == 0)ret = eap_peer_otp_register();
#endif /* EAP_OTP */#ifdef EAP_SIMif (ret == 0)ret = eap_peer_sim_register();
#endif /* EAP_SIM */#ifdef EAP_LEAPif (ret == 0)ret = eap_peer_leap_register();
#endif /* EAP_LEAP */#ifdef EAP_PSKif (ret == 0)ret = eap_peer_psk_register();
#endif /* EAP_PSK */#ifdef EAP_AKAif (ret == 0)ret = eap_peer_aka_register();
#endif /* EAP_AKA */#ifdef EAP_AKA_PRIMEif (ret == 0)ret = eap_peer_aka_prime_register();
#endif /* EAP_AKA_PRIME */#ifdef EAP_FASTif (ret == 0)ret = eap_peer_fast_register();
#endif /* EAP_FAST */#ifdef EAP_PAXif (ret == 0)ret = eap_peer_pax_register();
#endif /* EAP_PAX */#ifdef EAP_SAKEif (ret == 0)ret = eap_peer_sake_register();
#endif /* EAP_SAKE */#ifdef EAP_GPSKif (ret == 0)ret = eap_peer_gpsk_register();
#endif /* EAP_GPSK */#ifdef EAP_WSCif (ret == 0)ret = eap_peer_wsc_register();
#endif /* EAP_WSC */#ifdef EAP_IKEV2if (ret == 0)ret = eap_peer_ikev2_register();
#endif /* EAP_IKEV2 */#ifdef EAP_VENDOR_TESTif (ret == 0)ret = eap_peer_vendor_test_register();
#endif /* EAP_VENDOR_TEST */#ifdef EAP_TNCif (ret == 0)ret = eap_peer_tnc_register();
#endif /* EAP_TNC */#ifdef EAP_PWDif (ret == 0)ret = eap_peer_pwd_register();
#endif /* EAP_PWD */#ifdef EAP_SERVER_IDENTITYif (ret == 0)ret = eap_server_identity_register();
#endif /* EAP_SERVER_IDENTITY */#ifdef EAP_SERVER_MD5if (ret == 0)ret = eap_server_md5_register();
#endif /* EAP_SERVER_MD5 */#ifdef EAP_SERVER_TLSif (ret == 0)ret = eap_server_tls_register();
#endif /* EAP_SERVER_TLS */#ifdef EAP_SERVER_UNAUTH_TLSif (ret == 0)ret = eap_server_unauth_tls_register();
#endif /* EAP_SERVER_UNAUTH_TLS */#ifdef EAP_SERVER_MSCHAPV2if (ret == 0)ret = eap_server_mschapv2_register();
#endif /* EAP_SERVER_MSCHAPV2 */#ifdef EAP_SERVER_PEAPif (ret == 0)ret = eap_server_peap_register();
#endif /* EAP_SERVER_PEAP */#ifdef EAP_SERVER_TLVif (ret == 0)ret = eap_server_tlv_register();
#endif /* EAP_SERVER_TLV */#ifdef EAP_SERVER_GTCif (ret == 0)ret = eap_server_gtc_register();
#endif /* EAP_SERVER_GTC */#ifdef EAP_SERVER_TTLSif (ret == 0)ret = eap_server_ttls_register();
#endif /* EAP_SERVER_TTLS */#ifdef EAP_SERVER_SIMif (ret == 0)ret = eap_server_sim_register();
#endif /* EAP_SERVER_SIM */#ifdef EAP_SERVER_AKAif (ret == 0)ret = eap_server_aka_register();
#endif /* EAP_SERVER_AKA */#ifdef EAP_SERVER_AKA_PRIMEif (ret == 0)ret = eap_server_aka_prime_register();
#endif /* EAP_SERVER_AKA_PRIME */#ifdef EAP_SERVER_PAXif (ret == 0)ret = eap_server_pax_register();
#endif /* EAP_SERVER_PAX */#ifdef EAP_SERVER_PSKif (ret == 0)ret = eap_server_psk_register();
#endif /* EAP_SERVER_PSK */#ifdef EAP_SERVER_SAKEif (ret == 0)ret = eap_server_sake_register();
#endif /* EAP_SERVER_SAKE */#ifdef EAP_SERVER_GPSKif (ret == 0)ret = eap_server_gpsk_register();
#endif /* EAP_SERVER_GPSK */#ifdef EAP_SERVER_VENDOR_TESTif (ret == 0)ret = eap_server_vendor_test_register();
#endif /* EAP_SERVER_VENDOR_TEST */#ifdef EAP_SERVER_FASTif (ret == 0)ret = eap_server_fast_register();
#endif /* EAP_SERVER_FAST */#ifdef EAP_SERVER_WSCif (ret == 0)ret = eap_server_wsc_register();
#endif /* EAP_SERVER_WSC */#ifdef EAP_SERVER_IKEV2if (ret == 0)ret = eap_server_ikev2_register();
#endif /* EAP_SERVER_IKEV2 */#ifdef EAP_SERVER_TNCif (ret == 0)ret = eap_server_tnc_register();
#endif /* EAP_SERVER_TNC */#ifdef EAP_SERVER_PWDif (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_EPOLLeloop.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_TRACEsignal(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_POLLint 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_EPOLLint 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_POLLint num_poll_fds;int timeout_ms = 0;
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECTfd_set *rfds, *wfds, *efds;    //fd_set 是 select 中用到的一种参数类型struct timeval _tv;
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLLint timeout_ms = -1;
#endif /* CONFIG_ELOOP_EPOLL */int res;struct os_reltime tv, now;#ifdef CONFIG_ELOOP_SELECTrfds = 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);elsetv.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_POLLnum_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_EPOLLif (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_POLLeloop_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_EPOLLeloop_sock_table_dispatch(eloop.epoll_events, res);
#endif /* CONFIG_ELOOP_EPOLL */}eloop.terminate = 0;
out:
#ifdef CONFIG_ELOOP_SELECTos_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,
};

深入理解 wpa_supplicant(二)相关推荐

  1. 深入理解wpa_supplicant

    转自:http://blog.csdn.net/innost/article/details/20862875 先感谢各位兄弟姐妹们的耐心等待.本书预计在3月中旬上市发售.从今天开始,我将在博客中连载 ...

  2. faster rcnn源码理解(二)之AnchorTargetLayer(网络中的rpn_data)

    转载自:faster rcnn源码理解(二)之AnchorTargetLayer(网络中的rpn_data) - 野孩子的专栏 - 博客频道 - CSDN.NET http://blog.csdn.n ...

  3. AQS理解之二,自己设计一个锁

    AQS理解之二,自己设计一个锁 一,实现锁的条件 首先我们想一想,如果我们自己实现一个类似于java中的锁,我们可能需要哪些必要的东西: 1,记录是哪个线程持有了锁. 2,如果有一个变量代表加锁,A线 ...

  4. 设计模式理解(二)创建型——单例、原型

    设计模式理解(二)单例(Singleton)与原型(Prototype) 为什么一起写,因为懒.... 单例,就是用了面向对象语言的一些奇技淫巧,把构造函数私有了,然后用一个自身类型的静态指针作为全局 ...

  5. Docker的一些理解(二)

    Docker的一些理解(二) 百度百科 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux或Windows操作系统的机器上 ...

  6. 本质安全设备标准(IEC60079-11)的理解(二)

    本质安全设备标准(IEC60079-11)的理解(二) 十,本安设备的测试 我们知道如何测试本安设备以及一些基本概念后, 现在需要进一步说明: (1),本安设备的测试和一般软件,硬件的测试是完全不同的 ...

  7. SVPWM算法理解(二)——关于非零基本矢量幅值和线电压幅值的解释

    SVPWM算法理解(二)--关于非零基本矢量幅值和线电压幅值的解释 1 引言 2 非零基本矢量的幅值 3 线电压的幅值 4 电压空间矢量图中的图形含义 5 如何保证逆变器的输出电压不失真 1 引言   ...

  8. DFT - 对芯片测试的理解(二) 详解

    DFT - 对芯片测试的理解(二) 详解 参考: https://www.docin.com/p-2014360649.html The basic view of DFT scan chain 这图 ...

  9. 深入理解 wpa_supplicant(四)

    本文为<深入理解Android Wi-Fi.NFC和GPS卷>读书笔记,Android源码为Android 5.1 android-5.1/external/wpa_supplicant_ ...

最新文章

  1. Linux C/C++ 链接选项之静态库--whole-archive,--no-whole-archive和--start-group, --end-group
  2. 我在华为做Android外包的真实经历!吊打面试官系列!
  3. C++模板剖析:函数模板、类模板解析
  4. 2018.5.28 PSOC第一枪:基于cypress的蓝牙开发
  5. 判断给定数字n是否为素数(质数)
  6. markdown使用markdown-viewer生成目录_谷歌浏览器查看m文件
  7. 如何用c++画图_画图教室 | 绘制Mapping第一步:美团搜索火锅串串香...认真的!...
  8. kafka面试题简答
  9. CRI 与 ShimV2:一种 Kubernetes 集成容器运行时的新思路
  10. 中国人口增长的数学模型(for数学建模)
  11. C语言100题练习计划 33——递归实现指数函数
  12. python html抓取,并用re正则表达式解析(一)
  13. 天津医科大学计算机研究生吧,天津医科大学
  14. mysql联合索引排序_对mysql联合索引中的字段进行合理排序
  15. Eric靶机渗透测试通关全教程
  16. rosdep update 错误
  17. WC2015 滚粗记
  18. vmware 网络不可达
  19. 2W销量Steam大神,光临联盟微信群,近距离分享成功之路!(聊天实录)
  20. 在Apple Watch上找不到“i”图标的解决方法

热门文章

  1. HarmonyOS UI开发 StackLayout(堆栈布局) 的使用
  2. ValueError: urls must start with a leading slash
  3. 关于Activity class {package/class} does not exist
  4. unigui的页面布局使用
  5. 验证插件——jquery.validate.js
  6. Oracle Database 11.2.0.4.0 已在 中标麒麟Linux x86-64 NeoKylin Linux Advanced Server 6 上通过认证...
  7. WPF的图片操作效果(一):RenderTransform
  8. Java学习笔记(二)不定时更新
  9. 解决Apache CXF 不支持传递java.sql.Timestamp和java.util.HashMap类型问题
  10. Sql存储过程加密和解密