一、Wpa_supplicant中的P2P
1.基本概论
- 由WifiStateMachine启动,wifi有两个服务WifiService和WifiP2pService,他们都和wpa_supplicant进行交互
- 启动点:在init配置文件中
service p2p supplicant /system/bin/p2p_supplicant
2.运行时的形态
- 其实就是wpa_supplicant,只不过是打开了P2P的功能
3.P2P 模块初始化代码分析
int wpas_p2p_init(struct wpa_global* global, struct wpa_supplicant *wpa_s)
{
struct p2p_config p2p; // P2p变量只想了一个p2p_config对象,代表P2p模块的配置信息
unsigned int r;
int i;
// (1)WPA_DRIVER_FLAGS_P2P_CAPABLE代表WIFI驱动对P2P支持的能力
if (global -> p2p) return 0;
// 如果wifi驱动能够完成P2p功能,就不用老家WPAS了
if (wpa_s -> drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {...}
// (2) 初始化并设置P2p_config对象
os_memset(&p2p, 0, sizeof(p2p));
p2p.msg_ctx = wpa_s;
p2p.cb_ctx = wpa_s;
p2p.p2p_scan = wpas_p2p_scan; // p2p对应的扫描函数
.... // 设置一些回调函数
os_memcpy(wpa_s -> global -> p2p_dev_addr, wpa_s -> own_addr, ETH_ALEN);
os_memcpy(p2p.dev_addr, wpa_s-> global -> p2p_dev_addr, ETH_ALEN);
// 设置P2p模块配置信息,包括device name、 model name 、 uuid等
p2p.dev_name = wpa_s -> conf -> device_name;
p2p.manufacturer = wpa_s -> conf -> manufacturer;
p2p.model_name = wpa_s -> conf -> model_name;
p2p.model_number = wap_s -> conf -> serial_number;
if (wpa_s -> wps) {
os_memecp(p2p.uuid, wpa_s -> wps -> uudi, 16);
p2p.config_methods = wpa_s -> wps -> config_methods;
}
// 设置Operation chanel信息和listen channel信息
if(wpa_s -> conf -> p2p_listen_reg_class &&
wpa_s -> conf -> p2p_listen_channel) {
p2p.reg_class = wpa_s -> confg -> p2p_listen_reg_class;
p2p.channel = wpa_s -> config -> P2p_listen_channel;
} else {
// 设置默认值
}
// 设置国家码
if (wpa_s -> conf -> country[0] && wpa_s -> conf -> country[1]) {
os_memcpy(p2p.country, wpa_s -> conf -> country, 2);
p2p.country[2] = 0x04;
} else {
// 配置文件中没有设置国家,所以取值为"XX\x04"
os_memcpy(p2p.country, "XX\x04", 3);
}
// 判断wifi驱动是否支持配置文件中设置的operation channel 和 listent channel
if (wpas_p2p_setup_channels(wpa_s, &p2p.channels)){ .... }
os_memcpy(p2p.pri_dev_type, wpa_s -> conf -> device_type, WPS_DEV_TYPE_LEN);
p2p.num_sec_dev_typs = wpa_s -> conf -> device_type;
os_memcpy(p2p.sec_dev_types * WPA_DEV_TYPE_LEN);
// 是否支持concurrent operation
p2p.concurrent_operations = !!(wpa_s -> drv_flags & WPA_DRIVER_FLAGS_P2P_CONCURRENT);
p2p.max_peers = 100; // 最多能报错100个对端p2p device信息
/*
配置文件中么有设置p2p_ssid_postfix, 但是p2pStateMachine在initializeP2pSettings函数中将设置p2p ssid后缀,
*/
if (wpa_s -> conf -> p2p_ssid_postfix) {......}
p2p.p2p_intra_bss = wpa_s -> conf -> 2p_intra_bss;
// (3) global -P2p指向一个p2p_data结构体,它是WPAS中p2p模块的代表
global -> p2p = p2p_init(&p2p);
.....
for(int i = 0; i < MAX_WPS_VENDOR_EXT; i++) {
// 复制vendor厂商特定的WSC属性信息
p2p_add_wps_vendor_extension(global -> p2p, wpa_s conf -> wps_vendor_ext[i]);
}
return 0;
}
- 上面其实就是初始化了一个p2p_config对象,根据p2p_supplicant.conf文件的信息来设置其中的内容,同时还需要为p2p模块设置一些回调函数
- p2p_init函数来初始化p2p模块
4.上面涉及到的一些知识
- 上面drv_flags变量,可以在驱动的头文件中获知一二
- 主要就是一个配置文件的标志,里面代表着这个设备是不是支持AP,是不是支持P2P,是不是支持共存,比如
#define WPA_DRIVER_FLAGS_AP 0x00000040 // wifi驱动是否支持AP,使得设备能够扮演GO的角色
#define WPA_DRIVER_FLAGS_P2P_CONCURRENT 0x000000200 // wifi驱动支持STA和P2P的并发运行
......
- 下面看下wps_p2p_init中出现几个重要数据结构
- p2p_config中定义了20个回调函数,这些函数定义了P2P模块和外界交互的接口
- 在wpas_p2p_init总,这些回调函数均指向了p2p_supplicant.c中的对应的函数,例如:p2p_scan指向了wpas_p2p_scan,dev_lost指向了wpas_dev_lost
- p2p_data指向了一个p2p_config对象
5.下面展示五种数据结构
- p2p_device代表一个p2p设备,其中设备名、Device capabilityBitmap等信息保存在一个类型为p2p_peer_info的对象中
- p2p_group代表一个P2p Group的信息,其内部包含一个P2p_group_config对象和一个P2p_group_member链表,P2p_gorup_config表示该group的配置信息,p2p_group_member代表Group Member即P2p client的信息
6.下面来看下p2p_init函数
struct p2p_data * p2p_init(const struct p2p_config *cfg)
{
struct p2p_data *p2p;
....
// 从下面可以看出,一个p2p_data对象的内存分布,该内存将包含一个p2p_data的所有信息以及一个p2p_config对象的所有信息
p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg));
// 将p2p_data的cfg成员变量指向保存p2p_config信息的那块内存地址
p2p -> cfg = (struct p2p_config*) (p2p + 1);
os_memcpy(p2p -> cfg, cfg, sizeof(*cfg)); // 复制传入的cfg
if (cfg -> dev_name) p2p -> cfg -> def_name = os_strdup(cfg -> dev_name);
... // 其他信息复制进去
#ifdef ANDROID_P2P
p2p -> min_disc_int = 2; // listen state的最小时间为200ms
p2p -> sd_dev_list = NULL;
#else
p2p -> min_disc_int = 1;
#endif
p2p -> max_disc_init = 3;
// 随机获取next_tie_breaker的初始值
// 第二个参数1表示next_tie_breaker的字节长度,其类型是u8
os_get_random(&p2p -> nex_tie_breaker, 1);
p2p -> next_tie_breaker &= 0x01;
// 设置本机p2p device的device capability信息
if (cfg -> sd_request) p2p -> dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
p2p ->dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE;
if (cfg -> concurrent_operations) // 支持concurrent功能
p2p -> dev_capab |= P2P_DEV_CAPAB_CONCURRENT_OPER;
p2p -> dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
dl_list_init(&p2p -> devices);
// 注册一个超时时间(如果定义了ANDROID_P2P的宏,该时间为30ms
// 用来检测是否有不活跃的p2p_device
eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, p2p_expiration_timeout, p2p, NULL);
return p2p;
}
后记:毕业已经五年有余,脱离了语数外物理化很多年,如果现在再让我拿起这些东西,可能读起来用晦涩难懂来形容一点不为过,反而研读历史、地理、政治、金融、法律一些书籍更为得心应手或者说孜孜不倦,这就是所谓兴趣驱动吧,不把它作为任务反而更有动力,但是一旦作为考核的对象,也许就兴趣大减。
二、源码
- gitee路径:https://gitee.com/dongqianrui/AndroidStudioProject/tree/master/Test1
- CSDN:https://blog.csdn.net/weixin_44630050
- 博客园:https://www.cnblogs.com/ruigege0000/
- 欢迎关注微信公众号:傅里叶变换,个人账号,仅用于技术交流