7.4.4 GO Negotiation流程分析

P2pStateMachine收到P2P_PROV_DISC_PBC_RSP_EVENT消息后,将在ProvisionDiscoveryState中调用p2pConnectWithPinDisplay,该函数内部将发送P2P_CONNECT命令给WPAS。马上来看该命令的处理流程。

1.P2P_CONNECT处理流程

P2P_CONNECT命令的参数比较多,而本例中P2pStateMachine发送的命令格式如下。

  1. P2P_CONNECT 8a329b6cd180 pbc go_intent=7
  2. //其中,"8a:32:9b:6c:d1:80"代表对端P2P设备地址
  3. //"pbc"指定了WSC配置方法为PBC,"go_intent=7"设置GO Intent值为7

P2P_CONNECT对应的处理函数为p2p_ctrl_connect,其代码如下所示。

[—>ctrl_iface.c::p2p_ctrl_connect]

  1. static int p2p_ctrl_connect(struct wpa_supplicant*wpa_s,
  2.   char *cmd,char *buf, size_t buflen)
  3. {
  4. u8 addr[ETH_ALEN]; char *pos, *pos2;
  5. char *pin = NULL; enum p2p_wps_method wps_method;
  6. int new_pin; int ret; int persistent_group;
  7. int join; int auth;
  8. int go_intent = -1; int freq = 0;
  9.  
  10. if (hwaddr_aton(cmd, addr)) return -1;
  11. ......// 参数处理,最终调用的函数为wpas_p2p_connect
  12. new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
  13. persistent_group, join, auth, go_intent,freq);
  14. ......
  15. os_memcpy(buf, "OK\n", 3);
  16. return 3;
  17. }

wpas_p2p_connect的代码如下所示。

[—>p2p_supplicant.c::wpas_p2p_connect]

  1. int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
  2. const char *pin, enum p2p_wps_method wps_method,
  3. int persistent_group, int join, int auth, int go_intent,int freq)
  4. {
  5. int force_freq = 0, oper_freq = 0;
  6. u8 bssid[ETH_ALEN];
  7. int ret = 0;
  8. enum wpa_driver_if_type iftype;
  9. const u8 *if_addr;
  10. ......
  11. if (go_intent < 0)go_intent = wpa_s->conf->p2p_go_intent;
  12. wpa_s->p2p_wps_method = wps_method;
  13.  
  14. wpa_s->p2p_pin[0] = '\0';
  15. // 本例中,由于GON还未完成,GO角色未能确定,所以join为0
  16. if (join) {......}
  17. ......// 频段设置
  18. /*
  19. 注意下面这个wpas_p2p_create_iface函数,它将判断是否需要创建一个新的virtual interface,还记得
  20. 7.4.1节介绍Driver Flags和重要数据结构时提到的WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P
  21. 标志吗?就本例而言,wifi driver flags中包含了该标志,所以下面这个函数的返回值为1,表示需要单独创建
  22. 一个新的virtual interface供P2P使用。这个virtual interface的地址应该就是P2P Interface Address。
  23. */
  24. wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
  25.  
  26. if (wpa_s->create_p2p_iface) { // 本例满足此if条件
  27. iftype = WPA_IF_P2P_GROUP; // 设置interface type
  28. if (go_intent == 15) iftype = WPA_IF_P2P_GO; // 本例的go_intent为7
  29. /*
  30. 下面这个函数将创建此virtual interface,并获取其interface address。以Galaxy Note 2
  31. 为例。P2P Device Address是“92:18:7c:69:88:e2”,而P2P Interface Address是
  32. “92:18:7c:69:08:e2”。
  33. wpas_p2p_add_group_interface内部将调用driver_nl80211.c的wpa_driver_nl80211_if_add
  34. 函数。感兴趣的读者不妨自行研究。
  35. */
  36. if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) return -1;
  37. if_addr = wpa_s->pending_interface_addr;
  38. } else
  39. if_addr = wpa_s->own_addr;
  40. .......
  41. // 下面这个函数内部将调用p2p_connect,我们将直接分析
  42. if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method,
  43. go_intent, if_addr, force_freq,persistent_group) < 0) {......}
  44. return ret;
  45. }

2.GON Request发送流程

来看p2p_connect函数,其代码如下所示。

[—>p2p.c::p2p_connect]

  1. int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
  2. enum p2p_wps_method wps_method,
  3. int go_intent, const u8 *own_interface_addr,
  4. unsigned int force_freq,
  5. int persistent_group)
  6. {
  7. struct p2p_device *dev;
  8. // 如果指定了工作频段,则需要判断是否支持该工作频段
  9. if (p2p_prepare_channel(p2p, force_freq) < 0) return -1;
  10.  
  11. p2p->ssid_set = 0;
  12. dev = p2p_get_device(p2p, peer_addr);
  13. ......// 设置dev的一些信息
  14. p2p->go_intent = go_intent;
  15. os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
  16. // 如果P2P模块的状态不为P2P_IDLE,则先停止find工作
  17. if (p2p->state != P2P_IDLE) p2p_stop_find(p2p);
  18. ......
  19. dev->wps_method = wps_method;
  20. dev->status = P2P_SC_SUCCESS;
  21. ......
  22.  
  23. if (p2p->p2p_scan_running) {
  24. /*
  25. 如果当前P2P还在扫描过程中,则设置start_after_scan为P2P_AFTER_SCAN_CONNECT标志,
  26. 当scan结束后,在扫描结果处理流程中,该标志将通知P2P进入connect处理流程。
  27. */
  28. p2p->start_after_scan = P2P_AFTER_SCAN_CONNECT;
  29. os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN);
  30. return 0;
  31. }
  32. p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
  33. // 下面这个函数将发送GON Request帧,直接来看该函数
  34. return p2p_connect_send(p2p, dev);
  35. }

[—>p2p_go_neg.c::p2p_connect_send]

  1. int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
  2. {
  3. struct wpabuf *req; int freq;
  4. ......
  5. req = p2p_build_go_neg_req(p2p, dev);
  6. p2p_set_state(p2p, P2P_CONNECT); // 设置P2P模块的状态为P2P_CONNECT
  7. // 设置pending_action_state为P2P_PENDING_GO_NEG_REQUEST
  8. p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST;
  9. p2p->go_neg_peer = dev; // 设置GON对端设备
  10. dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE;
  11. dev->connect_reqs++;
  12. #ifdef ANDROID_P2P
  13. dev->go_neg_req_sent++;
  14. #endif
  15. // 发送GON Request帧
  16. if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
  17. p2p->cfg->dev_addr, dev->info.p2p_device_addr,
  18. wpabuf_head(req), wpabuf_len(req), 200) < 0) {......}
  19. ......
  20. return 0;
  21. }

至此,GON Request帧就将发送给对端P2P设备。图7-36描述了P2P_CONNECT命令的处理流程。

7.4.4 GO Negotiation流程分析 - 图1

图7-36 P2P_CONNECT命令处理流程

3.GON Response帧处理流程

根据前面对Action帧接收流程的分析可知,收到的GON Response帧将在p2p_process_go_neg_resp函数中被处理。该函数的代码如下所示。

[—>p2p_go_neg.c::p2p_process_go_neg_resp]

  1. void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
  2. const u8 *data, size_t len, int rx_freq)
  3. {
  4. struct p2p_device *dev; struct wpabuf *conf; int go = -1;
  5. struct p2p_message msg; u8 status = P2P_SC_SUCCESS;
  6. int freq;
  7. dev = p2p_get_device(p2p, sa);
  8. // 解析GON Response帧
  9. if (p2p_parse(data, len, &msg)) return;
  10. ......// 参数检查
  11. /*
  12. 下面这个函数将利用图7-13计算谁来扮演GO。返回值大于0,表示本机扮演GO,
  13. 返回-1表示双方都想成为GO,返回值为0,表示对端扮演GO。本例中,假设GO为本机设备。
  14. */
  15. go = p2p_go_det(p2p->go_intent, *msg.go_intent);
  16. ......// 参数检查,内容比较繁杂,感兴趣的读者请自行研究
  17. if (go) {......// 处理工作频段}
  18.  
  19. p2p_set_state(p2p, P2P_GO_NEG);// 设置P2P模块的状态为P2P_GO_NEG
  20. p2p_clear_timeout(p2p);
  21. ......
  22. fail:
  23. // 构造GON Confirmation帧
  24. conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, status,
  25. msg.operating_channel, go);
  26. p2p_parse_free(&msg);
  27. .......
  28. if (status == P2P_SC_SUCCESS) {
  29. p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM;
  30. dev->go_state = go ? LOCAL_GO : REMOTE_GO;// 本机扮演GO
  31. } else
  32. p2p->pending_action_state = P2P_NO_PENDING_ACTION;
  33. ......
  34. // 发送GON Confirmation帧
  35. if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa,
  36. wpabuf_head(conf), wpabuf_len(conf), 200) < 0) {......}
  37. wpabuf_free(conf);
  38. }

p2p_process_go_neg_resp实际的代码比较复杂,建议初学者先了解上面代码所涉及的大体流程。

当GON Confirmation帧发送出去后,wifi driver将向WPAS发送一个NL80211_CMD_FRAME_TX_STATUS消息,而该消息将导致driver wrapper发送EVENT_TX_STATUS消息给WPAS。下面我们直接来看EVENT_TX_STATUS的处理流程。

4.EVENT_TX_STATUS处理流程

在events.c中,和P2P以及EVENT_TX_STATUS相关的处理函数是offchannel_send_action_tx_status,该函数的代码如下所示。

[—>offchannel.c::offchannel_send_action_tx_status]

  1. void offchannel_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
  2. const u8 *data,size_t data_len, enum offchannel_send_action_result result)
  3. {
  4. ......// 参数检查
  5. wpabuf_free(wpa_s->pending_action_tx);
  6. wpa_s->pending_action_tx = NULL;
  7. /*
  8. 注意下面这个pending_action_tx_status_cb参数,它是一个函数指针,P2P每次发送Action的时候,
  9. 都会设置该变量。其真实的函数为wpas_p2p_send_action_tx_status(在wpas_send_action
  10. 函数中设置)
  11. */
  12. if (wpa_s->pending_action_tx_status_cb) {
  13. wpa_s->pending_action_tx_status_cb( wpa_s, wpa_s->pending_action_freq,
  14. wpa_s->pending_action_dst, wpa_s->pending_action_src,
  15. wpa_s->pending_action_bssid, data, data_len, result);
  16. }
  17. }

来看wpas_p2p_send_action_tx_status,其代码如下所示。

[—>p2p_supplicant.c::wpas_p2p_send_action_tx_status]

  1. static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s,
  2. unsigned int freq,const u8 *dst, const u8 *src, const u8 *bssid,
  3. const u8 *data, size_t data_len, enum offchannel_send_action_result result)
  4. {
  5. enum p2p_send_action_result res = P2P_SEND_ACTION_SUCCESS;
  6. ......
  7. // 重要函数:p2p_send_action_cb
  8. p2p_send_action_cb(wpa_s->global->p2p, freq, dst, src, bssid, res);
  9. ......
  10. }

p2p_send_action_cb的代码如下所示。

[—>p2p.c::p2p_send_action_cb]

  1. void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
  2. const u8 *src, const u8 *bssid,enum p2p_send_action_result result)
  3. {
  4. enum p2p_pending_action_state state;
  5. int success;
  6. success = result == P2P_SEND_ACTION_SUCCESS;
  7. // 读者还记得该变量的值吗?它应该是P2P_PENDING_GO_NEG_CONFIRM
  8. state = p2p->pending_action_state;
  9.  
  10. p2p->pending_action_state = P2P_NO_PENDING_ACTION;
  11. switch (state) {
  12. case P2P_NO_PENDING_ACTION:
  13. break;
  14. case P2P_PENDING_GO_NEG_REQUEST:
  15. // 读者可自行研究此函数。当发送完GON Request帧后,此函数也会被触发
  16. p2p_go_neg_req_cb(p2p, success);
  17. break;
  18. ......
  19. case P2P_PENDING_GO_NEG_CONFIRM:
  20. p2p_go_neg_conf_cb(p2p, result);// 分析这个函数
  21. break;
  22. ......// 其他case处理
  23. }
  24. }

来看p2p_go_neg_conf_cb函数,代码如下所示。

[—>p2p.c::p2p_go_neg_conf_cb]

  1. static void p2p_go_neg_conf_cb(struct p2p_data *p2p,
  2. enum p2p_send_action_result result)
  3. {
  4. struct p2p_device *dev;
  5. /*
  6. 每次收到TX Report后,都需要调用send_action_cb,其对应的真实函数是wpas_send_action_done。
  7. */
  8. p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
  9. ......
  10. dev = p2p->go_neg_peer;
  11. if (dev == NULL) return;
  12.  
  13. p2p_go_complete(p2p, dev);
  14. }

p2p_go_complete函数比较简单,其代码如下所示。

[—>p2p.c::p2p_go_complete]

  1. void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
  2. {
  3. struct p2p_go_neg_results res; int go = peer->go_state == LOCAL_GO;
  4. struct p2p_channels intersection; int freqs;
  5. size_t i, j;
  6. ......// 设置频段等参数
  7.  
  8. p2p_set_state(p2p, P2P_PROVISIONING);// 进入Group Formation的Provisioning阶段
  9. // go_neg_completed对应的函数是wpas_go_neg_completed
  10. p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
  11. }

[—>p2p_supplicant.c::wpas_go_neg_compeleted]

  1. void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
  2. {
  3. struct wpa_supplicant *wpa_s = ctx;
  4. ......
  5. // 下面这个函数将导致P2pStateMachine收到P2P_GO_NEGOTIATION_SUCCESS_EVENT消息
  6. wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS);
  7. wpas_notify_p2p_go_neg_completed(wpa_s, res);
  8.  
  9. if (wpa_s->create_p2p_iface) {// 本例满足此if条件
  10. /*
  11. 再创建一个wpa_supplicant对象。感兴趣的读者可自行研究下面这个函数。其内部将调用4.4.3节
  12. 分析的wpa_supplicant_add_iface函数。
  13. */
  14. struct wpa_supplicant *group_wpa_s =
  15. wpas_p2p_init_group_interface(wpa_s, res->role_go);
  16. ......
  17. // 如果本机扮演GO,则启动WSC Registrar功能,否则启动Enrollee功能
  18. // 下面这两个函数留给读者自行分析
  19. if (res->role_go) wpas_start_wps_go(group_wpa_s, res, 1);
  20. else wpas_start_wps_enrollee(group_wpa_s, res);
  21. } ......
  22. wpa_s->p2p_long_listen = 0;
  23. eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
  24. eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
  25. // Group Formation的超时时间为15秒左右
  26. eloop_register_timeout(15 + res->peer_config_timeout / 100,
  27. (res->peer_config_timeout % 100) * 10000,
  28. wpas_p2p_group_formation_timeout, wpa_s, NULL);
  29. }

当Group Negotiation完成后,WPAS将新创建一个wpa_supplicant对象,它将用于管理和操作专门用于P2P Group的virtual interface,此处有几点请读者注意。

·4.3.1节中介绍过,一个interface对应一个wpa_supplicant对象。

·此处新创建的wpa_supplicant对象用于GO,即扮演AP的角色,专门处理和P2P Group相关的事情,其MAC地址为P2P Interface Address。

·之前使用的wpa_supplicant用于非P2P Group操作,其MAC地址为P2P Device Address。

至此,整个EVENT_TX_STATUS处理流程就分析完毕了,其内容比较复杂。图7-37整理了此过程中一些重要的函数调用。

注意,图7-37实际上描述的是GON Confirmation帧对应的EVENT_TX_STATUS处理流程,和其他EVENT_TX_STATUS处理流程相比,前面7个函数调用都是一样的。

7.4.4 GO Negotiation流程分析 - 图2

图7-37 EVENT_TX_STATUS处理流程