几个重要的结构体介绍:

1. struct wpa_interface --- Parameters for wpa_supplicant_add_iface().

wpa_interface对应网络接口。因为wpa_supplicant支持多个网络接口,所以可能有多个wpa_interface结构体,可以通过命令行指定不同的接口。wpa_supplicant在main函数开始的地方会进行遍历!(参考代码main.c)

2. struct wpa_global --- Internal, global data for all %wpa_supplicant interfaces.

This structure is initialized by calling wpa_supplicant_init() when starting  starting %wpa_supplicant.

3. struct wpa_params --- Parameters for wpa_supplicant_init().

wpa_params主要记录一些与网卡本身没关的参数设置;

struct wpa_global {
struct wpa_supplicant *ifaces;
struct wpa_params params;
struct ctrl_iface_global_priv *ctrl_iface;
struct ctrl_iface_dbus_priv *dbus_ctrl_iface;
};

1. struct wpa_supplicant --- Internal data for wpa_supplicant interface.

每个网络接口都有一个对应的wpa_supplicant数据结构,该指针指向最近加入的一个,在wpa_supplicant数据结构中有指针指向next。

2. struct ctrl_iface_global_priv --- Global control interface

3. struct ctrl_iface_dbus_priv --- DBUS control interface

init.rc中有语句:

import /init.${ro.hardware}.rc

其中ro.hardware为qcom,所以导入的文件名为init.qcom.rc。

查看./device/qcom/common/rootdir/etc/init.qcom.rc文件,有以下代码:

service wpa_supplicant /system/bin/logwrapper /system/bin/wpa_supplicant \
-iwlan0 -Dnl80211 -c/data/misc/wifi/wpa_supplicant.conf -dddd \
-e/data/misc/wifi/entropy.bin
# we will start as root and wpa_supplicant will switch to user wifi
# after setting up the capabilities required for WEXT
# user wifi
# group wifi inet keystore
class main
socket wpa_wlan0 dgram wifi wifi
disabled
oneshot

当start supplicant后,就会调用main.c::main方法,对这些代码进行解析。

ps:通过property_set("ctl.start",”wpa_supplicant”)语句即可

---------------------------------------------------------------------------------------

初始化

初始化工作主要是由main()函数来执行。

在这个函数中,主要做了四件事:

a. 解析命令行传进的参数。

b. 调用wpa_supplicant_init()函数,做wpa_supplicant的初始化工作。

c. 调用wpa_supplicant_add_iface()函数,增加网络接口。

d. 调用wpa_supplicant_run()函数,让wpa_supplicant真正的run起来。

ps:最开始调用的os_program_init函数,主要就是来给wpa_supplicant进程分配权限。

定义在[-->./src/utils/os.h],实现在[--->./src/utils/os_unix.c]

解析命令行传进的参数

调用getopt函数循环解析参数:

c = getopt(argc, argv, "b:Bc:C:D:de:f:g:hi:KLNo:O:p:P:qstuvW");

函数原型: int getopt(int argc, char * const argv[], const char *optstring);

该函数调用一次,返回一个选项。在命令行选项参数再也检查不到optstring中包含的选项时,返回-1。

具体参考文章:http://www.cnitblog.com/zouzheng/archive/2007/04/02/25034.aspx

wpa_supplicant_init  分析

1.open debug files. [--->./src/utils/wpa_debug.c ]

wpa_debug_open_file   /  wpa_debug_open_syslog
2.register eap peer methods. [--->./wpa_supplicant/eap_register.c]
eap_peer_md5_register
eap_peer_tls_register
eap_peer_mschapv2_register
eap_peer_peap_register
eap_peer_ttls_register
eap_peer_gtc_register
eap_peer_otp_register
eap_peer_sim_register
eap_peer_leap_register
eap_peer_psk_register
eap_peer_aka_register
eap_peer_aka_prime_register
eap_peer_fast_register
...

3. 为wpa_global对象申请内存.[--->./wpa_supplicant/wpa_supplicant_i.h]

(1) 分析wpa_global结构体

struct wpa_global {
/*
*每个网络接口都有一个对应的wpa_supplicant数据结构,该指针指向最近加入的一个。*在wpa_supplicant数据结构中有指针指向next。
*/
  struct wpa_supplicant *ifaces;
  
  /*启动命令行中带的通用的参数*/
  struct wpa_params params;
  /*global 的控制接口*/
  struct ctrl_iface_global_priv *ctrl_iface;
  /*dbus 的控制接口*/
struct wpas_dbus_priv *dbus;
  ......
}

4. 设置wpa_global中的wpa_params中的参数

通过wpa_supplicant_init方法的参数params为wpa_global中的wpa_params中的参数赋值。

5. 调用eloop_init函数。这个函数将全局变量eloop中的user_data指针指向wpa_global。

ps:所说的user_data指针可能是其他结构体中的变量。

eloop 是一个结构体变量,看下其结构体:

struct eloop_data {
int max_sock; int count; /* sum of all table counts */
#ifdef CONFIG_ELOOP_POLL
int 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 */
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;
int reader_table_changed;
}; static struct eloop_data eloop;

6. 调用wpa_supplicant_global_ctrl_iface_init函数:初始化global的控制接口。

[--->./wpa_supplicant/ctrl_iface_unix.c]

global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global);

对于第一个接口的初始化,实际上通过socket进行了内部进程间通信,如下:

priv->sock = android_get_control_socket(global->params.ctrl_interface);
/* 此处通过getenv获得了sockfd(android平台),相当于如果本身有了fd的话,将直接跳转到havesock,如果没有的话,将创建连接,如下所示 */
priv->sock = socket(PF_UNIX, SOCK_DGRAM, ); /* PF_UNIX 代表内部进程间通信 */

下面bind或者connect,错误基本上也是goto到fail。到这里控制接口初始化结束。

7. 调用wpas_notify_supplicant_initialized函数:初始化dbus的控制接口。

[--->./wpa_supplicant/notify.c]
int wpas_notify_supplicant_initialized(struct wpa_global *global)
{
#ifdef CONFIG_DBUS
if (global->params.dbus_ctrl_interface) {
global->dbus = wpas_dbus_init(global);
if (global->dbus == NULL)
return -;
}
#endif /* CONFIG_DBUS */ return ;
}

8.最终返回一个wpa_global对象。

over~~

网上有说:下面还有一步是将该daemon的pid写入pid_file中。但是在android4.1上,这一步骤并不是在wpa_supplicant_init函数中实现,而是在wpa_supplicant_daemon函数中。具体执行流程是:

main.c::main ---> wpa_supplicant_run ---> wpa_supplicant_daemon 实现了该功能。

wpa_supplicant_add_iface():增加网络接口

该函数根据启动命令行中带有的参数增加网络接口, 有几个就增加几个。

1. struct wpa_supplicant wpa_s = wpa_supplicant_alloc();

wpa_supplicant是与网络接口对应的重要的数据结构,这里通过wpa_supplicant_alloc函数分配一个wpa_supplicant数据结构的内存,并对其中一些变量进行init:

static struct wpa_supplicant * wpa_supplicant_alloc(void)
{
struct wpa_supplicant *wpa_s; wpa_s = os_zalloc(sizeof(*wpa_s));
if (wpa_s == NULL)
return NULL;
  /* scan_req就是wpa定时去读driver scan到的结果。
   * scan_req=1,表示手动scan,即使conf文件中没有配置任何网络。
   * 除了0,还可以设置为2,好象设置2时不做关联请求(associate req)
   */
  wpa_s->scan_req = ;
  /* time in sec between scans to find suitable AP
   * 就是设置driver调度scan的间隔时间,若要省电时,可将改时间设置的长一些
   */
wpa_s->scan_interval = ;
wpa_s->new_connection = ;
wpa_s->parent = wpa_s;
wpa_s->sched_scanning = ; return wpa_s;
}

2. 调用wpa_supplicant_init_iface()函数:来做网络接口的初始工作。包括以下内容:

a. 调用wpa_config_read函数:

(1)读取配置文件:/data/misc/wifi/wpa_supplicant.conf

(解析命令行时的-c 选项,会将该配置文件添加进来)

(2)并将其中的信息解析到struct wpa_supplicant数据结构的wpa_s->conf中,这个conf是一个wpa_config类型的数据结构;

b. eapol_sm_notify_portEnabled / eapol_sm_notify_portValid

init eapol state machine,但是目前sm==NULL,所以什么也没做

ps:EAPOL state machine负责处理PTK 4-way HS和GTK 2-way HS  (这个不懂,这里不继续深入)

c. 调用wpa_supplicant_set_driver函数:完成驱动指定,设置驱动参数。

该函数会调用select_driver函数,其中分析下面这句代码:

/* 指向某个struct wpa_driver_ops */
wpa_s->driver = wpa_drivers[i];

wpa_drivers定义在./src/drivers/drivers.c中,

wpa_driver_ops定义在./src/drivers/driver.h

ops这个结构体对象注册了一系列wext(我们采用wext的驱动类型)相关的函数指针,从而提供了相应的驱动接口!

d. wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);

调用相应驱动的init函数。

e. 调用wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param)函数:

设置driver的param参数

f. 调用wpa_drv_get_ifname()函数:获得网络接口的名称。对于wext类型的driver,没有这个接口函数;

g. 调用wpa_supplicant_init_wpa()函数:初始化wpa,并做相应的初始化工作

h. 调用wpa_supplicant_driver_init()函数:初始化driver接口参数。

在该函数的最后,会执行以下代码:

wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
...
wpa_supplicant_req_scan(wpa_s, interface_count, );
...

这样可以来主动发起scan。

i. 调用wpa_supplicant_ctrl_iface_init()函数:初始化控制接口。

对于UNIX SOCKET这种方式,其本地socket文件是由配置文件里的ctrl_interface参数指定的路径加上网络接口名称。

wpa_supplicant_run()函数:真正启动 wpa_supplicant

1. 调用wpa_supplicant_daemon函数:将该daemon的pid写入pid_file中。

2. 调用eloop_run函数

a.将许多socket注册到eloop event模块:

在wpa_supplicant中,有许多与外界通信的socket,它们都是需要注册到eloop event模块中的,具体地说,就是在eloop_sock_table中增加一项记录,其中包括了sock_fd, handle, eloop_data, user_data。

b.利用select机制管理socket通信:

eloop event模块就是将这些socket组织起来,统一管理,然后在eloop_run中利用select机制来管理socket的通信。

终于结束了-.-!!! 到这里,wpa_supplicant的初始化就介绍完了...真正启动起来了...

wpa_supplicant 初始化的更多相关文章

  1. Android4.4 wpa_supplicant深入分析之wpa_supplicant初始化流程续

    下面我们将接上一篇文章继续分析main中第二个关键函数wpa_supplicant_add_iface. wpa_supplicant_add_iface用于向wpa_supplicant添加接口设备 ...

  2. wpa_supplicant软件架构分析

    wpa_supplicant软件架构分析 1. 启动命令 wpa supplicant 在启动时,启动命令可以带有很多参数,目前我们的启动命令如下: wpa_supplicant /system/bi ...

  3. wpa_supplicant 使用

    (1)通过adb命令行,可以直接打开supplicant,从而运行wpa_cli,可以解决客户没有显示屏而无法操作WIFI的问题,还可以避免UI的问题带到driver.进一步来说,可以用在很多没有键盘 ...

  4. 上层应用与wpa_supplicant,wpa_supplicant与kernel 相关socket创建交互分析

    单独拿出来,分析以下上层应用与wpa_supplicant   wpa_supplicant与kernel 的socket交互. 关联上层应用与wpa_supplicant的socket的创建.连接流 ...

  5. wpa_supplicant与kernel交互

    wpa_supplicant与kernel交互的操作,一般需要先明确驱动接口,以及用户态和kernel态的接口函数,以此来进行调用操作.这里分为4个步骤讨论. 1.首先需要明确指定的驱动接口.因为有较 ...

  6. wap_supplicant介绍

    目前可以使用wireless-tools 或wpa_supplicant工具来配置无线网络.请记住重要的一点是,对无线网络的配置是全局性的,而非针对具体的接口. wpa_supplicant是一个较好 ...

  7. android4.1 Wifi 浅析

    简单分析下wifi相关类,首先了解几个主要概念 AsyncChannel 简单理解: AsyncChannel,就是借助Messenger 机制,让两个不同的handler之间进行通信. AsyncC ...

  8. I.MX6 AW-NB177NF wifi HAL 调试修改

    /************************************************************************* * I.MX6 AW-NB177NF wifi H ...

  9. hostapd源代码分析(一):网络接口和BSS的初始化

    [转]hostapd源代码分析(一):网络接口和BSS的初始化 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004349 最近在做一 ...

随机推荐

  1. 分布式压测系列之Jmeter4.0第一季

    1)Jmeter4.0介绍 jmeter是个纯java编写的开源压测工具,apache旗下的开源软件,一开始是设计为web测试的软件,由于发展迅猛,现在可以压测许多协议比如:http.https.so ...

  2. Django-rest-framework(六)filter,ordering,search

    filter queryset 使用request.user相关的queryset class PurchaseList(generics.ListAPIView): serializer_class ...

  3. sql*plus

    [sql*plus创建txt文档编辑sql语句] (1)创建一个txt,命名doc SQL> ed  doc;          /*ed   文件名*/ (2)在doc.txt文件编辑sql语 ...

  4. Windows无法安装到这个磁盘 选中的磁盘具有MBR分区表解决方法

    在安装 win10的时候,会出现这种提示:Windows 无法安装到这个磁盘.选中的磁 盘具有 MBR 分区表.在 EFI 系统上, Windows 只能安装到 GPT 磁盘.出现这种 情况主要是因为 ...

  5. 蒜头君学英语--set()练习

    题目描述 蒜头君快要考托福了,这几天,蒜头君每天早上都起来记英语单词.花椰妹时不时地来考一考蒜头君:花椰妹会询问蒜头君一个单词,如果蒜头君背过这个单词,蒜头君会告诉花椰妹这个单词的意思,不然蒜头君会跟 ...

  6. For macOS.百度网盘 破解SVIP、下载速度限制~

    For macOS.百度网盘 破解SVIP.下载速度限制~ 是插件的 https://github.com/CodeTips/BaiduNetdiskPlugin-macOS 2019-01-03 让 ...

  7. webpack打包之后背景图不显示的问题

    修改build/utils.js文件里面的ExtractTextPlugin,添加:publicPath: ‘…/…/’,具体代码如下:

  8. SSM框架理解搭建(虽然是网上拼的,但是实际按照搭建是可以的)——

    SpringSpring就像是整个项目中装配bean的大工厂,在配置文件中可以指定使用特定的参数去调用实体类的构造方法来实例化对象.Spring的核心思想是IoC(控制反转),即不再需要程序员去显式地 ...

  9. 【Python让生活更美好01】os与shutil模块的常用方法总结

    Python作为一种解释型的高级语言,脚本语言,又被称作“胶水语言”,就是因为其灵活的语法和其依靠浩如烟海的第三方包实现的丰富多彩的功能,而os和shutil就是这样一种功能强大的模块,可以非常快捷地 ...

  10. 剑指offer题目系列一

    本篇介绍<剑指offer>第二版中的四个题目:找出数组中重复的数字.二维数组中的查找.替换字符串中的空格.计算斐波那契数列第n项. 这些题目并非严格按照书中的顺序展示的,而是按自己学习的顺 ...