P2pStateMachine的ProvisionDiscoveryState在其EA中將發送形如"P2P_PROV_DISC 8a:32:9b:6c:d1:80 pbc"的命令給WPAS(請參考7.3.2節“CONNECT處理流程分析”)去執行,其核心處理函數是p2p_ctrl_prov_disc,代碼如下所示。
**ctrl_iface.c::p2p_ctrl_prov_disc**
~~~
static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd)
{
u8 addr[ETH_ALEN]; char *pos;
if (hwaddr_aton(cmd, addr)) return -1;
.....// 參數處理。P2P_PROV_DISC命令的完整參數形式為“<addr> <config method> [join]”
// 其最后一個join參數為可選項。WifiP2pService沒有使用它
// 調用wpas_p2p_prov_disc,其內部將調用p2p_prov_disc_req,我們直接來看它
return wpas_p2p_prov_disc(wpa_s, addr, pos, os_strstr(pos, "join") != NULL);
}
~~~
**1、PD Request幀發送流程**
p2p_prov_disc_req的代碼如下所示。
**p2p.c::p2p_prov_disc_req**
~~~
int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
u16 config_methods, int join, int force_freq)
{
struct p2p_device *dev;
dev = p2p_get_device(p2p, peer_addr);// 根據目標設備地址找到對應的p2p_device對象
if (dev == NULL)
dev = p2p_get_device_interface(p2p, peer_addr);
......
dev->wps_prov_info = 0;
dev->req_config_methods = config_methods;
// 就本例而言,join的值為0
if (join) dev->flags |= P2P_DEV_PD_FOR_JOIN;
else dev->flags &= ~P2P_DEV_PD_FOR_JOIN; // 取消dev->flags中的P2P_DEV_PD_FOR_JOIN標志
......
p2p->user_initiated_pd = !join;
if (p2p->user_initiated_pd && p2p->state == P2P_IDLE)
p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
// 最后調用p2p_send_prov_disc_req發送數據
return p2p_send_prov_disc_req(p2p, dev, join, force_freq);
}
~~~
p2p_send_prov_disc_req比較簡單,代碼如下。
**p2p_pd.c::p2p_send_prov_disc_req**
~~~
int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
int join, int force_freq)
{
struct wpabuf *req; int freq;
// 確定對端設備所在的工作頻段
if (force_freq > 0) freq = force_freq;
else freq = dev->listen_freq > 0 ? dev->listen_freq :dev->oper_freq;
......
dev->dialog_token++;// 還記得表7-3關于Dialog Token的描述嗎
if (dev->dialog_token == 0) dev->dialog_token = 1;
// 構造Provision Discovery Request幀內容
req = p2p_build_prov_disc_req(p2p, dev->dialog_token,
dev->req_config_methods,join ? dev : NULL);
......
p2p->pending_action_state = P2P_PENDING_PD;// 該標志表明當前pending的Action是PD
// p2p_send_action內部將調用wpas_send_action。這部分內容比較簡單,請讀者自行研究
// 另外,第7.4.2節也會提到wpas_send_action函數,讀者也可學習完本章后再來研究它
if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
p2p->cfg->dev_addr, dev->info.p2p_device_addr,
wpabuf_head(req), wpabuf_len(req), 200) < 0) {......}
// 保存對端P2P設備地址
os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN);
wpabuf_free(req);
return 0;
}
~~~
上述代碼中,PD Request幀最終將通過p2p_send_action函數發送出去。不過,p2p_send_action并不簡單,它將涉及Off Channel發送以及處理對應netlink消息的過程。做為本書Wi-Fi部分的最后一章,請讀者在學習完本章后,再自行研究這個函數。
圖7-34展示了PD Request幀發送流程中的重要函數調用序列。
:-: 
圖7-34 PD Request幀發送流程
圖7-34列出了wpas_send_action中的幾個重要函數調用,請讀者自行研究時候注意相關內容。
下面來看PD Respone幀的處理流程。由于PD Response屬于Action幀,所以我們將介紹WPAS中Action幀的接收流程,然后再分析PD Response的處理流程。
**2、Action幀接收流程**
PD Response幀屬于Public Action幀的一種,而根據7.4.1節“注冊Action幀監聽事件”的分析可知,當收到對端設備發來的PD Response幀后,process_bss_event函數將被調用。此函數的代碼如下所示。
**driver_nl80211.c::process_bss_event**
~~~
static int process_bss_event(struct nl_msg *msg, void *arg)
{
struct i802_bss *bss = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *tb[NL80211_ATTR_MAX + 1];
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
switch (gnlh->cmd) {
case NL80211_CMD_FRAME: // 收到對端發送的幀
case NL80211_CMD_FRAME_TX_STATUS: // 對應本機所發送的管理幀的TX Report
mlme_event(bss->drv, gnlh->cmd, tb[NL80211_ATTR_FRAME],
tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
tb[NL80211_ATTR_COOKIE]);
break;
......
}
return NL_SKIP;
}
~~~
由上述代碼可知,不論是代表由本機所發送的管理幀TX Report的NL80211_CMD_FRAME_TX_STATUS消息,還是代表本機接收到對端發來的管理幀事件的NL80211_CMD_FRAME消息,最終都會調用mlme_event函數,其代碼如下所示。
**driver_nl80211.c::mlme_event**
~~~
static void mlme_event(struct wpa_driver_nl80211_data *drv, enum nl80211_commands cmd,
struct nlattr *frame, struct nlattr *addr, struct nlattr *timed_out,
struct nlattr *freq, struct nlattr *ack,struct nlattr *cookie)
{
......
switch (cmd) {
case NL80211_CMD_AUTHENTICATE:
mlme_event_auth(drv, nla_data(frame), nla_len(frame));
break;
case NL80211_CMD_ASSOCIATE:
mlme_event_assoc(drv, nla_data(frame), nla_len(frame));
break;
......
case NL80211_CMD_FRAME:
mlme_event_mgmt(drv, freq, nla_data(frame), nla_len(frame));
break;
case NL80211_CMD_FRAME_TX_STATUS:
mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
nla_len(frame), ack);
......
}
}
~~~
mlme_event將處理各種類型的幀事件。對于本例而言,此時將調用mlme_event_mgmt函數,其代碼如下所示。
**driver_nl80211.c::mlme_event_mgmt**
~~~
static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv,
struct nlattr *freq, const u8 *frame, size_t len)
{
const struct ieee80211_mgmt *mgmt;
union wpa_event_data event;
u16 fc, stype;
mgmt = (const struct ieee80211_mgmt *) frame;
......
fc = le_to_host16(mgmt->frame_control);
stype = WLAN_FC_GET_STYPE(fc);
os_memset(&event, 0, sizeof(event));
if (freq) {
event.rx_action.freq = nla_get_u32(freq);
drv->last_mgmt_freq = event.rx_action.freq;
}
if (stype == WLAN_FC_STYPE_ACTION) {
event.rx_action.da = mgmt->da;
event.rx_action.sa = mgmt->sa;
event.rx_action.bssid = mgmt->bssid;
event.rx_action.category = mgmt->u.action.category;
event.rx_action.data = &mgmt->u.action.category + 1;
event.rx_action.len = frame + len - event.rx_action.data;
// EVENT_RX_ACTION幀代表ACTION幀
wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event);
} else {
event.rx_mgmt.frame = frame;
event.rx_mgmt.frame_len = len;
// EVENT_RX_MGMT代表其他類型的管理幀
wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
}
}
~~~
wpa_supplicant_event處理EVENT_RX_ACTION的內容比較豐富,不過對于P2P來說,wpa_supplicant_event將調用wpas_p2p_rx_action,而wpas_p2p_rx_action又會調用p2p_rx_action,所以此處直接看p2p_rx_action函數即可,其代碼如下所示。
**p2p.c::p2p_rx_action**
~~~
void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
const u8 *bssid, u8 category, const u8 *data, size_t len, int freq)
{
if (category == WLAN_ACTION_PUBLIC) {// 處理Public Action幀
p2p_rx_action_public(p2p, da, sa, bssid, data, len, freq);
return;
}
......// 參數檢查
switch (data[0]) {// P2P規范使用的其他非Public類型的Action幀
case P2P_NOA:
break;
case P2P_PRESENCE_REQ:
p2p_process_presence_req(p2p, da, sa, data + 1, len - 1, freq);
break;
case P2P_PRESENCE_RESP:
p2p_process_presence_resp(p2p, da, sa, data + 1, len - 1);
break;
case P2P_GO_DISC_REQ:
p2p_process_go_disc_req(p2p, da, sa, data + 1, len - 1, freq);
break;
......
}
}
~~~
上述代碼中專門處理Public Action幀的p2p_rx_action_public函數代碼如下所示。
**p2p.c::p2p_rx_action_public**
~~~
static void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da,
const u8 *sa, const u8 *bssid, const u8 *data,size_t len, int freq)
{
......
switch (data[0]) {
case WLAN_PA_VENDOR_SPECIFIC:// P2P Public Action滿足此條件
......
p2p_rx_p2p_action(p2p, sa, data + 1, len - 1, freq);
break;
case WLAN_PA_GAS_INITIAL_REQ:
p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq);
break;
......// 其他類型的Public Action幀處理
}
}
~~~
p2p_rx_p2p_action函數是P2P模塊中Public Action幀得到分類處理的最后一關,其代碼如下所示。
**p2p.c::p2p_rx_p2p_action**
~~~
static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq)
{
switch (data[0]) { // P2P支持的Public Action幀在此處得到分類和相應處理
case P2P_GO_NEG_REQ: // 處理GON Request幀
p2p_process_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq);
break;
case P2P_GO_NEG_RESP: // 處理GON Response幀
p2p_process_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq);
break;
case P2P_GO_NEG_CONF: // 處理GON Confirmation幀
p2p_process_go_neg_conf(p2p, sa, data + 1, len - 1);
break;
case P2P_INVITATION_REQ: // 處理Invitation Request幀
p2p_process_invitation_req(p2p, sa, data + 1, len - 1,rx_freq);
break;
case P2P_INVITATION_RESP: // 處理Invitation Response幀
p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
break;
case P2P_PROV_DISC_REQ: // 處理PD Request幀
p2p_process_prov_disc_req(p2p, sa, data + 1, len - 1, rx_freq);
break;
case P2P_PROV_DISC_RESP: // 處理PD Response幀
p2p_process_prov_disc_resp(p2p, sa, data + 1, len - 1);
break;
case P2P_DEV_DISC_REQ: // 處理Device Discoverability Request幀
p2p_process_dev_disc_req(p2p, sa, data + 1, len - 1, rx_freq);
break;
case P2P_DEV_DISC_RESP: // 處理Device Discoverability Response幀
p2p_process_dev_disc_resp(p2p, sa, data + 1, len - 1);
break;
......// default語句
}
}
~~~
至此,Action幀的接收流程就介紹完了。整體而言,這部分代碼難度不大,但是調用函數卻比較多。圖7-35總結了這部分流程所涉及的一些重要函數。
:-: 
圖7-35 Action幀接收流程
由圖7-35可知,p2p_rx_p2p_action為P2P Public Action幀處理邏輯的總入口,如果后文分析時碰到其他類型的P2P Public Action幀,我們將直接轉入該函數來分析。
**3、PD Response幀處理流程**
由上述的p2p_rx_p2p_action可知,PD Response幀對應的處理函數是p2p_process_prov_disc_resp,其代碼如下所示。
**p2p_pd.c::p2p_process_prov_disc_resp**
~~~
void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len)
{
struct p2p_message msg;
struct p2p_device *dev;
u16 report_config_methods = 0;
// 解析PD Response幀
if (p2p_parse(data, len, &msg)) return;
// 獲取對應的P2P Device對象
dev = p2p_get_device(p2p, sa);
......
/*
當前我們pending的action是PD,由于已經收到了PD Response,所以可以置pending_action_state
變量為P2P_NO_PENDING_ACTION。
*/
if (p2p->pending_action_state == P2P_PENDING_PD) {
os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
}
if (dev->dialog_token != msg.dialog_token)return;
if (p2p->user_initiated_pd && os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0)
p2p_reset_pending_pd(p2p);
/*
如果所要求的WSC方法和PD Response返回的WSC方法不一致,則表明對端P2P設備不支持所要求的WSC方法。
*/
if (msg.wps_config_methods != dev->req_config_methods) {
// 調用wpas_prov_disc_fail,以處理PD失敗的情況
// 不過WPAS中,該函數沒有干什么有意義的事情
if (p2p->cfg->prov_disc_fail)
p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,P2P_PROV_DISC_REJECTED);
p2p_parse_free(&msg);
goto out;
}
report_config_methods = dev->req_config_methods;
dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | P2P_DEV_PD_PEER_KEYPAD);
......
dev->wps_prov_info = msg.wps_config_methods;
p2p_parse_free(&msg);
out:
dev->req_config_methods = 0;
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);// 請讀者自行研究send_action_done函數
if (p2p->cfg->prov_disc_resp)// prov_disc_respz指向wpas_prov_disc_resp
p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,report_config_methods);
}
~~~
馬上來看wpas_prov_disc_resp函數,其代碼如下所示。
**p2p_supplicant.c::wpas_prov_disc_resp**
~~~
void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
{
struct wpa_supplicant *wpa_s = ctx;
unsigned int generated_pin = 0;
/*
pending_pd_before_join變量對應于這樣一種場景:即GON已經完成,但WSC配置方法還沒有確定。
在后文分析GON時,我們將見到這種場景。
*/
if (wpa_s->pending_pd_before_join &&
(os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
os_memcmp(peer, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) {
wpa_s->pending_pd_before_join = 0;
wpas_p2p_join_start(wpa_s);
return;
}
if (config_methods & WPS_CONFIG_DISPLAY)
wpas_prov_disc_local_keypad(wpa_s, peer, "");
else if (config_methods & WPS_CONFIG_KEYPAD) {
generated_pin = wps_generate_pin();
wpas_prov_disc_local_display(wpa_s, peer, "", generated_pin);
} else if (config_methods & WPS_CONFIG_PUSHBUTTON)
wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR,MAC2STR(peer));
......
}
~~~
對于WSC PBC方法而言,wpa_msg將發送P2P_EVENT_PROV_DISC_PBC_RESP(字符串,值為"P2P-PROV-DISC-PBC-RESP")消息給客戶端,這也觸發了7.3.2節分析P2P_PROV_DISC_PBC_RSP_EVENT處理流程中所描述的工作流程。
現在來看本章關于P2P的最后一到工序。
- 前言
- 第1章 準備工作
- 1.1 Android系統架構
- 1.2 工具使用
- 1.2.1 Source Insight的使用
- 1.2.2 Eclipse的使用
- 1.2.3 BusyBox的使用
- 1.3 本書資源下載說明
- 第2章 深入理解Netd
- 2.1 概述
- 2.2 Netd工作流程
- 2.2.1 main函數分析
- 2.2.2 NetlinkManager分析
- 2.2.3 CommandListener分析
- 2.2.4 DnsProxyListener分析
- 2.2.5 MDnsSdListener分析
- 2.3 CommandListener中的命令
- 2.3.1 iptables、tc和ip命令
- 2.3.2 CommandListener構造函數和測試工具ndc
- 2.3.3 InterfaceCmd命令
- 2.3.4 IpFwd和FirewallCmd命令
- 2.3.5 ListTtysCmd和PppdCmd命令
- 2.3.6 BandwidthControlCmd和IdletimerControlCmd命令
- 2.3.7 NatCmd命令
- 2.3.8 TetherCmd和SoftapCmd命令
- 2.3.9 ResolverCmd命令
- 2.4 NetworkManagementService介紹
- 2.4.1 create函數詳解
- 2.4.2 systemReady函數詳解
- 2.5 本章總結和參考資料說明
- 2.5.1 本章總結
- 2.5.2 參考資料說明
- 第3章 Wi-Fi基礎知識
- 3.1 概述
- 3.2 無線電頻譜和802.11協議的發展歷程
- 3.2.1 無線電頻譜知識
- 3.2.2 IEEE 802.11發展歷程
- 3.3 802.11無線網絡技術
- 3.3.1 OSI基本參考模型及相關基本概念
- 3.3.2 802.11知識點導讀
- 3.3.3 802.11組件
- 3.3.4 802.11 Service介紹
- 3.3.5 802.11 MAC服務和幀
- 3.3.6 802.11 MAC管理實體
- 3.3.7 無線網絡安全技術知識點
- 3.4 Linux Wi-Fi編程API介紹
- 3.4.1 Linux Wireless Extensions介紹
- 3.4.2 nl80211介紹
- 3.5 本章總結和參考資料說明
- 3.5.1 本章總結
- 3.5.2 參考資料說明
- 第4章 深入理解wpa_supplicant
- 4.1 概述
- 4.2 初識wpa_supplicant
- 4.2.1 wpa_supplicant架構
- 4.2.2 wpa_supplicant編譯配置
- 4.2.3 wpa_supplicant命令和控制API
- 4.2.4 git的使用
- 4.3 wpa_supplicant初始化流程
- 4.3.1 main函數分析
- 4.3.2 wpa_supplicant_init函數分析
- 4.3.3 wpa_supplicant_add_iface函數分析
- 4.3.4 wpa_supplicant_init_iface函數分析
- 4.4 EAP和EAPOL模塊
- 4.4.1 EAP模塊分析
- 4.4.2 EAPOL模塊分析
- 4.5 wpa_supplicant連接無線網絡分析
- 4.5.1 ADD_NETWORK命令處理
- 4.5.2 SET_NETWORK命令處理
- 4.5.3 ENABLE_NETWORK命令處理
- 4.6 本章總結和參考資料說明
- 4.6.1 本章總結
- 4.6.2 參考資料說明
- 第5章 深入理解WifiService
- 5.1 概述
- 5.2 WifiService的創建及初始化
- 5.2.1 HSM和AsyncChannel介紹
- 5.2.2 WifiService構造函數分析
- 5.2.3 WifiStateMachine介紹
- 5.3 加入無線網絡分析
- 5.3.1 Settings操作Wi-Fi分析
- 5.3.2 WifiService操作Wi-Fi分析
- 5.4 WifiWatchdogStateMachine介紹
- 5.5 Captive Portal Check介紹
- 5.6 本章總結和參考資料說明
- 5.6.1 本章總結
- 5.6.2 參考資料說明
- 第6章 深入理解Wi-Fi Simple Configuration
- 6.1 概述
- 6.2 WSC基礎知識
- 6.2.1 WSC應用場景
- 6.2.2 WSC核心組件及接口
- 6.3 Registration Protocol詳解
- 6.3.1 WSC IE和Attribute介紹
- 6.3.2 802.11管理幀WSC IE設置
- 6.3.3 EAP-WSC介紹
- 6.4 WSC代碼分析
- 6.4.1 Settings中的WSC處理
- 6.4.2 WifiStateMachine的處理
- 6.4.3 wpa_supplicant中的WSC處理
- 6.4.4 EAP-WSC處理流程分析
- 6.5 本章總結和參考資料說明
- 6.5.1 本章總結
- 6.5.2 參考資料說明
- 第7章 深入理解Wi-Fi P2P
- 7.1 概述
- 7.2 P2P基礎知識
- 7.2.1 P2P架構
- 7.2.2 P2P Discovery技術
- 7.2.3 P2P工作流程
- 7.3 WifiP2pSettings和WifiP2pService介紹
- 7.3.1 WifiP2pSettings工作流程
- 7.3.2 WifiP2pService工作流程
- 7.4 wpa_supplicant中的P2P
- 7.4.1 P2P模塊初始化
- 7.4.2 P2P Device Discovery流程分析
- 7.4.3 Provision Discovery流程分析
- 7.4.4 GO Negotiation流程分析
- 7.5 本章總結和參考資料說明
- 7.5.1 本章總結
- 7.5.2 參考資料說明
- 第8章 深入理解NFC
- 8.1 概述
- 8.2 NFC基礎知識
- 8.2.1 NFC概述
- 8.2.2 NFC R/W運行模式
- 8.2.3 NFC P2P運行模式
- 8.2.4 NFC CE運行模式
- 8.2.5 NCI原理
- 8.2.6 NFC相關規范
- 8.3 Android中的NFC
- 8.3.1 NFC應用示例
- 8.3.2 NFC系統模塊
- 8.4 NFC HAL層討論
- 8.5 本章總結和參考資料說明
- 8.5.1 本章總結
- 8.5.2 參考資料說明
- 第9章 深入理解GPS
- 9.1 概述
- 9.2 GPS基礎知識
- 9.2.1 衛星導航基本原理
- 9.2.2 GPS系統組成及原理
- 9.2.3 OMA-SUPL協議
- 9.3 Android中的位置管理
- 9.3.1 LocationManager架構
- 9.3.2 LocationManager應用示例
- 9.3.3 LocationManager系統模塊
- 9.4 本章總結和參考資料說明
- 9.4.1 本章總結
- 9.4.2 參考資料說明
- 附錄