回顾一下 wpas 创建 socket 之后监听连接
=>wpa_supplicant_add_iface
==>wpa_supplicant_init_iface
wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s);
===>wpas_ctrl_iface_open_sock
eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv);
来看一下 eloop_register_read_sock 的实现
int eloop_register_read_sock(int sock, eloop_sock_handler handler,
void *eloop_data, void *user_data)
{
return eloop_register_sock(sock, EVENT_TYPE_READ, handler,
eloop_data, user_data);
}
int eloop_register_sock(int sock, eloop_event_type type,
eloop_sock_handler handler,
void *eloop_data, void *user_data)
{
struct eloop_sock_table *table;
// 获取到全局变量 struct eloop_data eloop 中该事件对应的 eloop_sock_table
table = eloop_get_sock_table(type);
return eloop_sock_table_add_sock(table, sock, handler,
eloop_data, user_data);
}
可以看到这里将 socket 根据事件类型加入到 eloop 的可读事件 table (readers) 中 ,这里需要看一下 eloop_sock_table 的结构:
struct eloop_sock_table {
int count; //所有监听 fd 的个数
//fd句柄及相关handler 的数组指针, 严格来说这里 table 指向的是一个eloop_sock 数组
struct eloop_sock *table;
eloop_event_type type; //该 table 监听事件类型
int changed;
};
struct eloop_sock {
int sock; //fd句柄
void *eloop_data;
void *user_data;
eloop_sock_handler handler; //event 处理函数
WSAEVENT event;
};
因为 wpas 的 socket 类型为基于 packages 的 DGRAM 方式,server 端只需监听单个的 socket 即可, 这里所有写到 server socket 的event 都会由 wpa_supplicant_ctrl_iface_receive 来处理。
static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,void *sock_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct ctrl_iface_priv *priv = sock_ctx;
char buf[4096]; int res; struct sockaddr_un from;
socklen_t fromlen = sizeof(from);
char *reply = NULL; size_t reply_len = 0; int new_attached = 0;
res = recvfrom(sock, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &from, &fromlen);
......
buf[res] = '\0';
// 客户端在调用 wpa_ctrl_attach 时, 会首先发送"ATTACH"命令
if (os_strcmp(buf, "ATTACH") == 0) {
......// 略过相关处理
}
......// "DETACH"和"LEVEL"命令处理
else {
// 除了上面3个命令其他的命令处理都在wpa_supplicant_ctrl_iface_process函数中
reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf,&reply_len);
}
if (reply) {// 回复客户端
sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,fromlen);
os_free(reply);
} ......
/*
Client成功ATTACH后, 将通知EAPOL模块。 因为有些认证流程需要用户的参与( 例如输入密码之类的) ,
所以当客户端连接上后, EAPOL模块将判断是否需要和客户端交互。 读者可阅读
eapol_sm_notify_ctrl_attached函数。
*/
if (new_attached)
eapol_sm_notify_ctrl_attached(wpa_s->eapol);
}
绝大部分命令都由wpa_supplicant_ctrl_iface_process函数处理 , 在搜索时上层会发送 “SCAN” , 来看一下其处理:
char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
char *buf, size_t *resp_len)
{
char *reply;
const int reply_size = 4096;
int reply_len;
......
reply = os_malloc(reply_size);
if (reply == NULL) {
*resp_len = 1;
return NULL;
}
os_memcpy(reply, "OK\n", 3);
reply_len = 3;
if (os_strcmp(buf, "PING") == 0) {
os_memcpy(reply, "PONG\n", 5);
reply_len = 5;
} else if (os_strcmp(buf, "IFNAME") == 0) {
reply_len = os_strlen(wpa_s->ifname);
os_memcpy(reply, wpa_s->ifname, reply_len);
}
......
} else if (os_strcmp(buf, "SCAN") == 0) {
wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len);
}
......
if (reply_len < 0) {
os_memcpy(reply, "FAIL\n", 5);
reply_len = 5;
}
*resp_len = reply_len;
return reply;
这里 “SCAN” 由 wpas_ctrl_scan 来处理
=> wpas_ctrl_scan
==> wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
{
// 参数sec 和 usec 代表延迟多长时间开始扫描, 这里上面传下来的都是 0
/* 查询当前 timeout 队列里已经有的相同任务,
如果延迟时间较新 request 长时以新任务取代,返回1,
当队列中的任务时间更早时则什么都不做, 返回0,
当找不到相同任务时, 返回-1,
*/
res = eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s,
NULL);
if (res == 1) {
wpa_dbg(wpa_s, MSG_DEBUG, "Rescheduling scan request: %d.%06d sec",
sec, usec);
} else if (res == 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Ignore new scan request for %d.%06d sec since an earlier request is scheduled to trigger sooner",
sec, usec);
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d.%06d sec",
sec, usec);
eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
}
}
scan request 最后会在 timeout 事件触发时由相应的 handler 来处理, 这里是由 wpa_supplicant_scan 来处理 ,这个函数内容非常的多, 会根据传进来的 wpa_s 中参数做不同的处理, 例如:当某些条件则不扫描,当正在扫描时延迟1s进行扫描, 设定扫描频段等。 最后执行的是 wpa_supplicant_trigger_scan :
====>wpa_supplicant_trigger_scan(wpa_s, scan_params);
// 实际上也是 timeout 事件
radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx)
=====>wpas_trigger_scan_cb
======>wpa_drv_scan(wpa_s, params);
wpa_drv_scan 会去调用 driver interface 中的 scan2
static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params)
{
if (wpa_s->driver->scan2)
return wpa_s->driver->scan2(wpa_s->drv_priv, params);
return -1;
}
从 Android.mk 中可以看到定义的是 CONFIG_DRIVER_NL80211 ,也就是 nl80211 接口 :
int wpa_driver_nl80211_scan(struct i802_bss *bss,
struct wpa_driver_scan_params *params)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret = -1, timeout;
struct nl_msg *msg = NULL;
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request");
drv->scan_for_auth = 0;
// 生成 nl_msg 并填充相应的参数 ,cmd 为 NL80211_CMD_TRIGGER_SCAN
msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params);
if (params->p2p_probe) {
struct nlattr *rates;
wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES);
if (nla_put(msg, NL80211_BAND_2GHZ, 8,
"\x0c\x12\x18\x24\x30\x48\x60\x6c"))
goto fail;
nla_nest_end(msg, rates);
if (nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE))
goto fail;
}
// 发送请求给wlan驱动。 返回值只是表示该命令是否正确发送给了驱动。 扫描结束事件将通过 driver event返回给WPAS。
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
......
drv->scan_state = SCAN_REQUESTED;
timeout = 10;
if (drv->scan_complete_events) {
timeout = 30;
}
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
// 一般情况下,driver完成扫描后需要通知WPAS一个scan complete事件。如果驱动不通知的话,WPAS就会自己去查询driver以获取扫描到的无线网络信息。
eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
drv, drv->ctx);
fail:
......
}
接下来 scan message经由netlink进入到Linux内核当中去处理。
这里的总体流程如下图(截图来自《深入理解Android:WIFI模块…》)
在搜索结束之后,waps 收到driver 的 NL80211_CMD_NEW_SCAN_RESULTS
D/wpa_supplicant( 1331): nl80211: Drv Event 34 (NL80211_CMD_NEW_SCAN_RESULTS) received for wlan0
case NL80211_CMD_NEW_SCAN_RESULTS:
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: New scan results available");
drv->scan_state = SCAN_COMPLETED;
drv->scan_complete_events = 1;
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
drv->ctx);
send_scan_event(drv, 0, tb);
==> wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
break;
wpa_supplicant_event被driver event模块用来发送driver事件给WPAS
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
case EVENT_SCAN_RESULTS:
.......
if (wpa_supplicant_event_scan_results(wpa_s, data))
==> _wpa_supplicant_event_scan_results(wpa_s, data, 1);
break; /* interface may have been removed */
.......
break;
}
static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
union wpa_event_data *data,
int own_request)
{
struct wpa_scan_results *scan_res = NULL;
wpa_supplicant_notify_scanning(wpa_s, 0);
// 获得无线网络扫描结果
scan_res = wpa_supplicant_get_scan_results(wpa_s,
data ? &data->scan_info :
NULL, 1);
if (scan_res == NULL) {
....... // 扫描结果为空, 重新发起扫描
wpa_supplicant_req_new_scan(wpa_s, 1, 0);
ret = -1;
goto scan_work_done;
}
wpa_supplicant_get_scan_results 中调用到 driver interface 中的 get_scan_results2 , 这个方法会向 driver 发 NL80211_CMD_GET_SCAN 并等待收到结果
D/wpa_supplicant( 1331): nl80211: Received scan results (14 BSSes)
static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
union wpa_event_data *data,
int own_request)
{
struct wpa_scan_results *scan_res = NULL;
......
wpa_supplicant_notify_scanning(wpa_s, 0);
scan_res = wpa_supplicant_get_scan_results(wpa_s,
data ? &data->scan_info :
NULL, 1);
.......
wpas_notify_scan_results(wpa_s);
wpas_notify_scan_done(wpa_s, 1);
来看一下获取到的扫描结果 wpa_scan_results 结构体
struct wpa_scan_results {
struct wpa_scan_res **res; //指向wpa_scan_res数组
size_t num; //res 数组个数
struct os_reltime fetch_time;
};
struct wpa_scan_res {
unsigned int flags;
u8 bssid[ETH_ALEN]; //无线网络的bssid
int freq; //工作频率
u16 beacon_int;
u16 caps;
int qual;
int noise;
int level;
u64 tsf;
unsigned int age; //距离上一次收到该bss信息过去的毫秒时间
unsigned int est_throughput;
int snr;
size_t ie_len;
size_t beacon_ie_len;
/* Followed by ie_len + beacon_ie_len octets of IE data */
};
然后向上层发送 WPA_EVENT_SCAN_RESULTS
D/wpa_supplicant( 1331): wlan0: New scan results available (own=1 ext=0)
D/wpa_supplicant( 1331): CTRL_IFACE monitor sent successfully to /data/misc/wifi/sockets/wpa_ctrl_950-2\x00
上层收到该消息之后会主动下 command 给 waps 获取搜索结果。