2024年3月7日发(作者:)
WIFI学习总结
1
2
WLAN技术 .............................................................................................................................. 3
802.11协议简述 ....................................................................................................................... 3
2.1.1 概述 ................................................................................................................... 3
3 802.11四种主要物理组件 ....................................................................................................... 4
3.1 工作站(Station) ....................................................................................................... 4
3.2 接入点(Access Point) .............................................................................................. 4
3.3 无线媒介(Wireless Medium) .................................................................................. 4
3.4 分布式系统(Distribution System) ........................................................................... 4
4 WIFI适配层 ............................................................................................................................. 5
5 6
5.1 wpa_ctrl的作用 ............................................................................................................ 6
5.2 WPA_SUPPLICANT .................................................................................................... 6
5.2.1 概念 ................................................................................................................... 6
5.2.2 Wpa_supplicant与驱动的交互 ........................................................................ 6
6 Wpa_cli调试工具 .................................................................................................................... 7
6.1 启动wpa_supplicant ..................................................................................................... 7
6.2 连接wpa_cli到wpa_supplicant .................................................................................. 7
6.3 示例 ............................................................................................................................... 8
6.3.1 无密钥认证AP ................................................................................................. 8
6.3.2 WEP认证AP ................................................................................................... 8
6.3.3 WPA-PSK/WPA2-PSK认证AP ...................................................................... 8
6.3.4 隐藏AP ............................................................................................................. 9
7 Wifi模块解析和启动流程 ....................................................................................................... 9
7.1 框架分析 ....................................................................................................................... 9
7.2 Wifi启动流程 ............................................................................................................ 11
8 WLAN驱动结构介绍 ............................................................................................................ 22
8.1 SDIO驱动 .................................................................................................................. 22
8.2 Boardcom无线通讯芯片 ........................................................................................... 23
8.2.1 概述 ................................................................................................................. 23
8.2.2 源码 ................................................................................................................. 23
8.3 详细接口及代码分析 ................................................................................................. 24
8.3.1 WIFI驱动流程分析 ....................................................................................... 24
8.3.2 WIFI设备注册流程 ....................................................................................... 25
8.3.3 WIFI驱动流程(二) ................................................................................... 40
8.3.4 网络设备注册流程 ......................................................................................... 43
9 IOCTL的调用逻辑 ................................................................................................................ 48
10 数据的传送 ..................................................................................................................... 56
10.1 数据传送过程简述 ................................................................................................. 56
10.2 Bcm4329芯片wlan驱动数据传送 .......................................................................... 57
10.3 传输超时 ................................................................................................................. 59
1
11 数据的接收 ..................................................................................................................... 59
11.1 数据接收的方式和过程 ............................................................................................. 59
11.2 选择哪种接收模式 ..................................................................................................... 60
11.3 Bcm4329芯片wlan驱动数据传送 .......................................................................... 60
12 电源管理相关的调用逻辑 ............................................................................................. 65
13 Android平台的Wifi模块移植要点 ............................................................................. 74
13.1 Wifi结构 .................................................................................................................... 74
13.2 Wifi模块环境 ............................................................................................................ 75
13.3 Wifi模块的编译......................................................................................................... 75
13.3.1 Wifi驱动源码 ................................................................................................. 75
13.3.2 在android平台添加BCM43xx驱动 ............................................................ 75
13.3.3 编译wifi驱动源码 ........................................................................................ 79
13.3.4 在android中使用BCM43xx ......................................................................... 80
2
1 WLAN技术
WLAN是英文WirelessLAN的缩写,就是无线局域网的意思。无线以太网技术是一种基于无线传输的局域网技术,与有线网络技术相比,具有灵活、建网迅速、个人化等特点。将这一技术应用于电信网的接入网领域,能够方便、灵活地为用户提供网络接入,适合于用户流动性较大、有数据业务需求的公共场所、高端的企业及家庭用户、需要临时建网的场合以及难以采用有线接入方式的环境等。
2 802.11协议简述
2.1.1 概述
作为全球公认的局域网权威,IEEE802工作组建立的标准在过去二十年内在局域网领域独领风骚。这些协议包括了802.3Ethernet协议、802.5TokenRing协议、802.3z100BASE-T快速以太网协议。在1997年,经过了7年的工作以后,IEEE发布了802.11协议,这也是在无线局域网领域内的第一个国际上被认可的协议。
在1999年9月,他们又提出了802.11b"HighRate"协议,用来对802.11协议进行补充,802.11b在802.11的1Mbps和2Mbps速率下又增加了5.5Mbps和11Mbps两个新的网络吞吐速率。利用802.11b,移动用户能够获得同Ethernet一样的性能、网络吞吐率、可用性。这个基于标准的技术使得管理员可以根据环境选择合适的局域网技术来构造自己的网络,满足他们的商业用户和其他用户的需求。802.11协议主要工作在ISO协议的最低两层上,并在物理层上进行了一些改动,加入了高速数字传输的特性和连接的稳定性。
主要内容:
1.802.11工作方式
2.802.11物理层
3.802.11b的增强物理层
4.802.11数字链路层
5.联合结构、蜂窝结构和漫游
3
3 802.11四种主要物理组件
3.1 工作站(Station)
构建网络的主要目的是为了在工作站间传送数据。所谓工作站,是指配备无线网络接口的计算设备。
3.2 接入点(Access Point)
802.11网络所使用的帧必须经过转换,方能被传递至其他不同类型的网络。具备无线至有线的桥接功能的设备称为接入点,接入点的功能不仅于此,但桥接最为重要。
3.3 无线媒介(Wireless Medium)
802.11标准以无线媒介在工作站之间传递帧。其所定义的物理层不只是一种,802.11最初标准化了两种射频物理层以及一种红外线物理层。
3.4 分布式系统(Distribution System)
当几个接入点串联以覆盖较大区域时,彼此之间必须相互通信以掌握移动式工作站的行踪。分布式系统属于802.11的逻辑组件,负责将帧转送至目的地。
下图为802.11网络的基本服务集(basic service set),其中包含了这四种物理组件。
4
4 WIFI适配层
里面定义很多字符串变量和适配层的接口实现,是对wpa_supplicant程序通信的接口封装,用来完成上层和wpa_supplicant的通信, 头文件在libhardware/include/hardware下,这里的函数用来向JNI的本地实现提供调用接口。
这里的函数,我把它们分为四类函数:
一类是命令相关的(控制)函数,就是在JNI层android_XXX_Command()函数所调用 的Wifi_Command()函数,调用流程如下:
android_XXX_command()=>docommand()=>wifi_command()=>wifi_send_command()=>wpa_ctrl_require()。
二类是 监听函数,即Wifi_wait_for_event()函数,调用流程如下:android_net_wifi_Waitforevent()=> wifi_wait_for_event()=>wpa_ctrl_recv()。
三就是WPA_SUPPLICANT的启动,连接,关闭函数
四是驱动的加载和卸载函数
5
5 wpa_supplicant
5.1 wpa_ctrl的作用
定义了两类套接字和一个管道,并分别实现了和wpa_supplicant的通信,而在实际的实现中采用的都是套接字的方式,因此wpa_supplicant适配层和wpa_supplicant层 是通过socket通讯的。
要是从wifi.c中真的很难看出它和wpa_supplicant有什么关系,和它联系密切的就是 这个wpa_ctrl.h文件,这里面定义了一个类wpa_ctrl,这个类中声明了两个Socket套接口,一个是本地一个是要连接的套接 口,wpa_ctrl与wpa_supplicant的通信就需要socket来帮忙了,而wpa_supplicant就是通过调用
wpa_ctrl.h中定义的函数和wpa_supplicant进行通讯的,wpa_ctrl类(其实是其中的两个socket)就是他们之间的桥梁。
5.2 WPA_SUPPLICANT
5.2.1 概念
wpa_supplicant本是开源项目源码,被谷歌修改后加入android移动平台,它主要是用来支持WEP,WPA/WPA2和WAPI无线协议和加密认证的,而实际上的工作内容是通过socket(不管是wpa_supplicant与上层还是wpa_supplicant与驱动都采用socket通讯)与驱动交互上报数据给用户,而用户可以通过socket发送命令给wpa_supplicant调动驱动来对WiFi芯片操作。 简单的说,wpa_supplicant就是WiFi驱动和用户的中转站外加对协议和加密认证的支持。
5.2.2 Wpa_supplicant与驱动的交互
5.2.2.1 wpa_supplicant.c
首先定义一个驱动操作数组externstruct wpa_driver_ops
*wpa_supplicant_drivers[],然后是系列wpa_supplicant_XXX()函数,很多函数里面调用 wpa_drv_XXX()函数,这些函数是wpa_supplicant_i.h中实现的函数。几乎每个函数都需要一个wpa_supplicant结 构,对其进行所有的控制和通信操作。
5.2.2.2 Wpa_supplicant_i.h
其中定义了一个重要数据结构wpa_supplicant,其中有一个重要的driver成
6
员,它是wpa_driver_ops类型,可以被用来调用抽象层的接口。接下来是系列函数声明,这些函数声明在wpa_supplicant.c中实现,然后就是wpa_drv_XXX函数,这些函数就是在 wpa_supplicant.c中被wpa_supplicant_xxx函数调用的,而这些wpa_drv_xxx函数也都有一个 wpa_supplicant结构的变量指针,用来调用封装的抽象接口,而这些抽象接口的实现在driver_wext.c中(如果使用的汉斯WEXT驱动)。
这里要注意的是:在wpa_suppliant.c文件中定义的很多函数是在该头文件中声明的,而不是在wpa_supplicant.h中声明的。
5.2.2.3 Driver_wext.c
对wpa_drvier_ops的个函数的具体实现,该结构指针在wpa_supplicant注册一个网络接口时会被初始化赋予指定的操作指针,wpa_supplicant.c中的wpa_supplicant_xxx函数通过wpa_supplicant结构中的该操作指针调用WEXT的实现接口。
就是在该文件中,创建了三个socket:ioctrl_socket,event_socket和mlme_socket,它们分别有自己的用途,如ioctrl_socket用于发送控制命令,event_socket用于监听驱动传来的event事件等。Wpa_supplicant通过这三个socket与wifi驱动关联,这里的socket同fd(文件描述符)类似。
6 Wpa_cli调试工具
6.1 启动wpa_supplicant
使用下面命令启动wpa_supplicant:
wpa_supplicant -Dwext
-c/data/misc/wifi/wpa_
-iwlan0 -C/data/system/wpa_supplicant
为了确保wpa_supplicant真的启动起来了,使用“ps”命令查看。
6.2
连接wpa_cli到wpa_supplicant
wpa_cli -p/data/system/wpa_supplicant -iwlan0
然后,就可以使用wpa_cli调试工具进行wifi调试了,下面列出了一些常用的调试命令:
7
>scan //扫描周围的AP
//显示扫描结果
//显示当前的连接状态信息
//终止wpa_supplicant
//退出wpa_cli
//返回可用network id
//设置网络
>scan_results
>status
>terminate
>quit
>add_network
>set_network
>select_network
>disable_network
>enable_network
//选择网络,禁用其它网络
//禁用网络
//启用网络
6.3 示例
6.3.1 无密钥认证AP
>add_network (返回可用network id, 假定返回0)
>set_network 0 ssid “666”
>set_network 0 key_mgmt NONE
>enable_network 0
>quit
如果上面的操作正确,我们会连接到一个AP,它的SSID为“666”,现在需要一个IP来访问internet:
dhcpcd wlan0
成功获取IP后,即可连上internet。
6.3.2 WEP认证AP
>add_network (假设返回1)
>set_network 1 ssid “666”
>set_network 1 key_mgmt NONE
>set_network 1 wep_key0 “ap passwork”
>set_network 1 wep_tx_keyidx 0
>select_network 1 (如果你已经连上了其它的AP,那么就需要这个命令来禁用其它的网络)
>enable_network 1
然后同上获取IP,连接到internet上。
6.3.3 WPA-PSK/WPA2-PSK认证AP
>add_network (假定返回2)
>set_network 2 ssid “666”
>set_network 2 psk “your pre-shared key”
8
>select_network 2
>enable_network 2
还有其它的命令进一步设置网络,不过wpa_supplicant已经给了我们一些默认的配置。
6.3.4 隐藏AP
原则上应该只要在上面的基础上去set_network netid scan_ssid 1即可,测试过无加密的Hidden AP,WEP/WPA/WPA2应该道理一样。
7 Wifi模块解析和启动流程
7.1 框架分析
WIFI整体框架如图所示:
9
WirelessSettings
WifiSetting AccessPointDialog
WifiEnabler
WIFI_STATE_CHANGED_ACTION
WifiLayer
NETWORK_STATE_CHANGED_ACTION
SCAN_RESULTS_AVAILABLE_ACTION
WifiManager WifiStateTracker
WifiService WifiMonitor
WifiNatvie
JAVA VM
Android_net_wifi_wifi
Wifi
Wpa_supplicant
Wifi模组
Wifi电源管理驱动
Kernel/net驱动
WiFi模块驱动.ko
首先,用户程序使用WifiManager类来管理Wifi模块,它能够获得Wifi模
10
块的状态,配置和控制Wifi模块,而所有这些操作都要依赖 Wifiservice类来实现。
WifiService和WifiMonitor类是Wifi框架的核心,如图所示。下面先来看看WifiService是什么时候,怎么被创建和初始化 的。
在systemServer启动之后,它会创建一个 ConnectivityServer对象,这个对象的构造函数会创建一个WifiService的实例,代码如下所示:
framework/base/services/java/com/android/server/
{
……
case _WIFI:
if (DBG) Slog.v(TAG, "Starting Wifi Service.");
WifiStateTracker wst = new WifiStateTracker(context,
mHandler); //创建WifiStateTracker实例
WifiService wifiService = newWifiService(context, wst);//创建WifiService实例
vice(_SERVICE, wifiService); //向服务管理系统添加Wifi服务
ifi(); //启动Wifi
mNetTrackers[_WIFI] = wst;
onitoring(); //启动WifiMonitor中的WifiThread线程
……
}
WifiService的主要工作:WifiMonitor和Wpa_supplicant的启动和关闭,向Wpa_supplicant发送命令。
WifiMonitor的主要工作:阻塞监听并接收来自Wpa_supplicant的消息,然后发送给WifiStateTracker。
上面两个线程通过AF_UNIX套接字和Wpa_supplicant通信,在通信过程中有两种连接方式:控制连接和监听连接。它们创建代码如下:
ctrl_conn =wpa_ctrl_open(ifname);
.. .. ..
monitor_conn = wpa_ctrl_open(ifname);
7.2 Wifi启动流程
(1)使能Wifi
要想使用Wifi模块,必须首先使能Wifi,当你第一次按下Wifi使能按
11
钮时,WirelessSettings会实例化一个WifiEnabler 对象,实例化代码如下:
packages/apps/settings/src/com/android/settings/
protected void onCreate(Bundle savedInstanceState) {
te(savedInstanceState);
……
CheckBoxPreferencewifi = (CheckBoxPreference)
findPreference(KEY_TOGGLE_WIFI);
mWifiEnabler= new WifiEnabler(this, wifi);
……
}
WifiEnabler类的定义大致如下,它实现了一个监听接口,当WifiEnabler对象被初始化后,它监听到你按键的动作,会调用响应函数 onPreferenceChange(),这个函数会调用WifiManager的setWifiEnabled()函数。
public class WifiEnabler erenceChangeListener {
……
public boolean onPreferenceChange(Preference preference,Object value) {
booleanenable = (Boolean) value;
……
if (iEnabled(enable)) {
bled(false);
……
}
……
}
我们都知道Wifimanager只是个服务代理,所以它会调用WifiService的setWifiEnabled()函数,而这个函数会调用 sendEnableMessage()函数,了解android消息处理机制的都知道,这个函数最终会给自己发送一个
MESSAGE_ENABLE_WIFI的消息,被WifiService里面定义的handlermessage()函数处理,会调用 setWifiEnabledBlocking()函数。下面是调用流程:
erencechange()=>ienabled()=>iEnabled()=>ableMessage()=>Message()=>iEnabledBlocking().
在 setWifiEnabledBlocking()函数中主要做如下工作:加载Wifi驱动,启动wpa_supplicant,注册广播接收器,启动 WifiThread监听线程。代码如下:
……
if (enable) {
if (!iver()) {
12
Slog.e(TAG, "Failed toload Wi-Fi driver.");
setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
return false;
}
if (!upplicant()) {
Driver();
Slog.e(TAG, "Failed tostart supplicant daemon.");
setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
return false;
}
registerForBroadcasts();
ventLoop();
……
至此,Wifi使能结束,自动进入扫描阶段。
(2) 扫描AP
当驱动加载成功后,如果配置文件的AP_SCAN = 1,扫描会自动开始,WifiMonitor将会从supplicant收到一个消息EVENT_DRIVER_STATE_CHANGED,调用 handleDriverEvent(),然后调用DriverStarted(),该函数向消息队列 添加EVENT_DRIVER_STATE_CHANGED,handlermessage()函数处理消息时调用scan()函数,并通过 WifiNative将扫描命令发送到wpa_supplicant。
Frameworks/base/wifi/java/android/net/wifi/
private void handleDriverEvent(Stringstate) {
if (state == null) {
return;
}
if (("STOPPED")) {
DriverStopped();
} else if (("STARTED")) {
DriverStarted();
} else if (("HANGED")) {
DriverHung();
}
}
Frameworks/base/wifi/java/android/net/wifi/
case EVENT_DRIVER_STATE_CHANGED:
switch(1) {
case DRIVER_STARTED:
13
/**
*Set the number of allowed radio channels according
*to the system setting, since it gets reset by the
*driver upon changing to the STARTED state.
*/
setNumAllowedChannels();
synchronized (this) {
if (mRunState == RUN_STATE_STARTING) {
mRunState = RUN_STATE_RUNNING;
if (!mIsScanOnly) {
reconnectCommand();
} else {
// In somesituations, supplicant needs to be kickstarted to
// start thebackground scanning
scan(true);
}
}
}
break;
上面是启动Wifi 时,自动进行的AP的扫描,用户当然也可以手动扫描AP,这部分实现在WifiService里面,WifiService通过startScan()接 口函数发送扫描命令到supplicant。
Frameworks/base/wifi/java/android/net/wifi/
public boolean startScan(booleanforceActive) {
enforceChangePermission();
switch (plicantState()) {
case DISCONNECTED:
case INACTIVE:
case SCANNING:
case DORMANT:
break;
default:
nResultHandling(
_SCAN_HANDLING_LIST_ONLY);
break;
}
return (forceActive);
}
然后下面的流程同上面的自动扫描,我们来分析一下手动扫描从哪里开始的。我们应该知道手动扫描是通过菜单键的扫描键来响应的,而响应该动作的
14
应该是 WifiSettings类中Scanner类的handlerMessage()函数,它调用WifiManager的 startScanActive(),这才调用WifiService的startScan()。
packages/apps/Settings/src/com/android/settings/
public boolean onCreateOptionsMenu(Menu menu) {
(, MENU_ID_SCAN, 0, _menu_scan)
.setIcon(_menu_scan_network);
(, MENU_ID_ADVANCED, 0, _menu_advanced)
.setIcon(_menu_manage);
return teOptionsMenu(menu);
}
当按下菜单键时,WifiSettings就会调用这个函数绘制菜单。如果选择扫描按钮,WifiSettings会调用 onOptionsItemSelected()。
packages/apps/Settings/src/com/android/settings/
public booleanonOptionsItemSelected(MenuItem item) {
switch (mId()) {
case MENU_ID_SCAN:
if(Enabled()) {
();
}
return true;
case MENU_ID_ADVANCED:
startActivity(new Intent(this,));
return true;
}
return onsItemSelected(item);
}
private class Scanner extends Handler {
private int mRetry = 0;
void resume() {
if (!hasMessages(0)) {
sendEmptyMessage(0);
}
}
void pause() {
mRetry = 0;
gress(false);
removeMessages(0);
}
15
@Override
public void handleMessage(Message message) {
if (canActive()){
mRetry = 0;
} else if (++mRetry >= 3) {
mRetry = 0;
xt(, _fail_to_scan,
_LONG).show();
return;
}
gress(mRetry != 0);
sendEmptyMessageDelayed(0, 6000);
}
}
这里的canActive()就会调用WifiService里 的startScan()函数,下面的流程和上面的一样,这里不赘述。
当supplicant完成了这个扫描命令后,它会发送一个消息给上 层,提醒他们扫描已经完成,WifiMonitor会接收到这消息,然后再发送给WifiStateTracker。
Frameworks/base/wifi/java/android/net/wifi/
void handleEvent(int event, String remainder) {
switch (event) {
caseDISCONNECTED:
handleNetworkStateChange(NECTED,remainder);
break;
case CONNECTED:
handleNetworkStateChange(TED,remainder);
break;
case SCAN_RESULTS:
ScanResultsAvailable();
break;
case UNKNOWN:
break;
}
}
WifiStateTracker将会广播 SCAN_RESULTS_AVAILABLE_ACTION消息:
16
Frameworks/base/wifi/java/android/net/wifi/
public voidhandleMessage(Message msg) {
Intent intent;
……
case EVENT_SCAN_RESULTS_AVAILABLE:
if(emReady()) {
oadcast(new
Intent(_RESULTS_AVAILABLE_ACTION));
}
sendScanResultsAvailable();
/**
* On receiving the first scanresults after connecting to
* the supplicant, switch scanmode over to passive.
*/
setScanMode(false);
break;
……
}
由于WifiSettings类注册了intent,能够处理SCAN_RESULTS_AVAILABLE_ACTION消息,它会调用 handleEvent(),调用流程如下所示。
Event()
nResults()
=>AccessPoints()
=>
=>
nResults()=>
sults() => sultsCommand()……
将 获取AP列表的命令发送到supplicant,然后supplicant通过Socket发送扫描结果,由上层接收并显示。这和前面的消息获取流程基本 相同。
(3)配置,连接AP
当用户选择一个活跃的AP时,WifiSettings响应打开一个对话框来配 置AP,比如加密方法和连接AP的验证模式。配置好AP后,WifiService添加或更新网络连接到特定的AP。
packages/apps/settings/src/com/android/settings/wifi/
public booleanonPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
if (preference instanceof AccessPoint) {
mSelected = (AccessPoint) preference;
showDialog(mSelected, false);
} else if (preference == mAddNetwork) {
mSelected = null;
17
showDialog(null, true);
} else if (preference == mNotifyOpenNetworks) {
(getContentResolver(),
_NETWORKS_AVAILABLE_NOTIFICATION_ON,
ked() ? 1 : 0);
} else {
return erenceTreeClick(screen, preference);
}
return true;
}
配置好以后,当按下“Connect Press”时,WifiSettings通过发送LIST_NETWORK命令到supplicant来检查该网络是否配置。如果没有该网络或没有配置 它,WifiService调用addorUpdateNetwork()函数来添加或更新网络,然后发送命令给supplicant,连接到这个网络。 下面是从响应连接按钮到WifiService发送连接命令的代码:
packages/apps/settings/src/com/android/settings/wifi/
public void onClick(DialogInterfacedialogInterface, int button) {
if (button == _FORGET && mSelected != null) {
forget(kId);
} else if (button == _SUBMIT && mDialog !=null) {
WifiConfiguration config = fig();
if (config == null) {
if (mSelected != null&& !requireKeyStore(fig())) {
connect(kId);
}
} else if (kId != -1) {
if (mSelected != null) {
Network(config);
saveNetworks();
}
} else {
int networkId =work(config);
if (networkId != -1) {
Network(networkId, false);
kId =networkId;
if ( || requireKeyStore(config)){
saveNetworks();
} else {
connect(networkId);
}
18
}
}
}
}
public intupdateNetwork(WifiConfiguration config) {
if(config == null || kId < 0) {
return -1;
}
return addOrUpdateNetwork(config);
}
private intaddOrUpdateNetwork(WifiConfiguration config) {
try {
return pdateNetwork(config);
} catch (RemoteException e) {
return -1;
}
}
pdateNetwork()通过调用
workVariable()将连接命令发送到Wpa_supplicant。
(4) 获取IP地址
当连接到supplicant后,WifiMonitor就会通知WifiStateTracker。
Frameworks/base/wifi/java/android/net/wifi/
Public void Run(){
if (connectToSupplicant()) {
// Send a message indicatingthat it is now possible to send commands
// to the supplicant
SupplicantConnection();
} else {
SupplicantLost();
return;
}
……
}
WifiStateTracker 发送EVENT_SUPPLICANT_CONNECTION消息到消息队列,这个消息有自己的handlermessage()函数处理,它会启动一个 DHCP线程,而这个线程会一直等待一个消息事件,来启动DHCP协议分配IP地址。
frameworks/base/wifi/java/android/net/wifi/
19
void notifySupplicantConnection() {
sendEmptyMessage(EVENT_SUPPLICANT_CONNECTION);
}
public void handleMessage(Message msg) {
Intent intent;
switch () {
case EVENT_SUPPLICANT_CONNECTION:
……
HandlerThread dhcpThread = newHandlerThread("DHCP Handler Thread");
();
mDhcpTarget = newDhcpHandler(per(), this);
……
……
}
当 Wpa_supplicant连接到AP后,它会发送一个消息给上层来通知连接成功,WifiMonitor会接受到这个消息并上报给 WifiStateTracker。
Frameworks/base/wifi/java/android/net/wifi/
void handleEvent(int event, String remainder) {
switch (event) {
case DISCONNECTED:
handleNetworkStateChange(NECTED,remainder);
break;
case CONNECTED:
handleNetworkStateChange(TED,remainder);
break;
……
}
private void handleNetworkStateChange(edState newState, String data) {
StringBSSID = null;
intnetworkId = -1;
if(newState == TED) {
Matcher match = r(data);
if(!()) {
if () Log.d(TAG, "Could not find BSSID in CONNECTEDevent
string");
}else {
BSSID = (1);
20
try {
networkId = nt((2));
} catch (NumberFormatException e) {
networkId = -1;
}
}
}
StateChange(newState,BSSID, networkId);
}
void notifyStateChange(DetailedState newState, StringBSSID, int networkId) {
Messagemsg = (
this, EVENT_NETWORK_STATE_CHANGED,
newNetworkStateChangeResult(newState, BSSID, networkId));
Target();
}
caseEVENT_NETWORK_STATE_CHANGED:
……
configureInterface();
……
private void configureInterface() {
checkPollTimer();
mLastSignalLevel = -1;
if(!mUseStaticIp) { //使用DHCP线程动态IP
if(!mHaveIpAddress && !mObtainingIpAddress) {
mObtainingIpAddress = true;
//发送启动DHCP线程获取IP
ptyMessage(EVENT_DHCP_START);
}
} else { //使用静态IP,IP信息从mDhcpInfo中获取
intevent;
if(ureInterface(mInterfaceName, mDhcpInfo)) {
mHaveIpAddress = true;
event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
if (LOCAL_LOGD) Log.v(TAG, "Static IP configurationsucceeded");
}else {
mHaveIpAddress = false;
event = EVENT_INTERFACE_CONFIGURATION_FAILED;
if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration failed");
}
sendEmptyMessage(event); //发送IP获得成功消息事件
21
}
}
DhcpThread获取EVENT_DHCP_START消息事件后,调用handleMessage()函数,启动DHCP获取IP地址的服务。
public void handleMessage(Message msg) {
intevent;
switch () {
case EVENT_DHCP_START:
……
Log.d(TAG, "DhcpHandler: DHCP requeststarted");
//启动一个DHCPclient的精灵进 程,为mInterfaceName请求分配一个IP地//址
if (p(mInterfaceName, mDhcpInfo)) {
event= EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
if(LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP request succeeded");
} else {
event= EVENT_INTERFACE_CONFIGURATION_FAILED;
Log.i(TAG,"DhcpHandler: DHCP request failed: " +
pError());
}
……
}
这 里调用了一个p()函数,NetworkUtils类是一个网络服务的辅助类,它主要定义了一些本地接口,这些接 口会通过他们的JNI层android_net_文件和DHCP client通信,并获取IP地址。
至此,IP 地址获取完毕,Wifi启动流程结束。
8 WLAN驱动结构介绍
8.1 SDIO驱动
在drivers/mmc下面是mmc卡,SD卡和SDIO卡驱动部分,其中包括host驱动,card驱动和core部分,由于网络接口卡挂接在SDIO总线上,所以在此之前我们先看一下SDIO的驱动结构。其驱动在drivers/mmc目录下的结构为:
|-- mmc
| |-- card
22
| |-- core
| |-- host
主要关注的目录是core目录,这个目录是真个驱动的核心目录,是媒体卡的通用代码部分,包括core.c,host.c和sdio.c等。CORE 层完成了不同协议和规范的实现,并为 HOST 层的驱动提供了接口函数,该目录完成sdio总线的注册操作,相应的ops操作,以及支持mmc的代码。详细的情况将在函数接口部分详细讨论。
Host目录是不同平台根据平台的特性而编写的host驱动。
8.2 Boardcom无线通讯芯片
8.2.1 概述
全球有线和无线通信半导体市场的领导者Broadcom(博通)公司(Nasdaq:BRCM)宣布, 推出最新无线组合芯片BCM4330,该芯片可支持更多媒体形式和数据应用,且不会增大智能手机、平板电脑及其他移动设备的尺寸或缩短其电池寿命。BCM4330在单个芯片上集成了业界领先的Broadcom 802.11n Wi-Fi、蓝牙和FM无线技术,与分立式半导体器件组成的解决方案相比,在成本、尺寸、功耗和性能上有显著优势,是移动设备的理想选择。
BCM4330采用了新的Wi-Fi和蓝牙标准,可支持新的、令人振奋的应用。例如,Broadcom
BCM4330是业界第一款经过蓝牙4.0标准认证的组合芯片解决方案, 集成了蓝牙低功耗(BLE)标准。该标准使蓝牙技术能以超低功耗运行,因此BCM4330非常适用于需要很长电池寿命的系统,如无线传感器、医疗和健身监控设备等。BCM4330还支持Wi-Fi Direct™和蓝牙高速(HS)标准,因此采用BCM4330的移动设备能直接相互通信,而不必先连接到接入点、成为传统网络的一部分,从而为很多无线设备之间新的应用和使用模式创造了机会。
Broadcom一直支持所有主流的操作系统(OS)平台,如Microsoft Windows和Windows
Phone、Google Chrome、Android等等,而且不仅是BCM4330,所有蓝牙、WLAN和GPS芯片组都提供这样的支持。
8.2.2 源码
Bcm4330驱动源码一般被厂商单独提供,如果要在开发的LINUX系统中(当然它还支持多种平台)使用该源码,可以添加到linux kernel源码树里,也可以单独组织存放,可以直接编译到kernel,也可以编译成模块,然后再系统启动的流程中或其他适当的实际加载到kernel中,一般建议单独组织并编译成模块在需要的时候加载如kernel。
|-- src
23
| |-- bcmsdio
| |-- dhd
| |-- dongle
| |-- include
| |-- shared
| |-- wl
这里主要内容到bcmsdio,dhd和wl三个目录下,bcm4330驱动的入口在dhd/sys/dhd_linux.c文件中的dhd_module()函数,设备的初始化和相关驱动注册都从这里开始,
8.3 详细接口及代码分析
8.3.1 WIFI驱动流程分析
以boardcom bcm4329芯片驱动为例,相应的函数流程图如下:
dhd_mudule_init
wl_android_init
Dhd_customer_gpio_wlan_ctrl
wl_android_wifictrl_func_add
Dhd_bus_register
24
8.3.2 WIFI设备注册流程
dhd_mudule_init
wl_android_wifictrl_func_add
wlan_add_card
Platform_driver_register(wifi_device[_legacy])
Platform_driver_register(wifi_device[_legacy])的调用将wifi_device[_legacy]驱动注册到系统中,wifi_device_legacy是为了兼容老版本的驱动。
Path: wl/sys/wl_android.c
Static struct Platform_driver wifi_device = {
.probe = wifi_probe
.remove = wifi_remove
.suspend = wifi_supend
.resume = wifi_resume
.driver = {
.name = “bcmdhd_wlan”
}
}
Static struct Platform_driver wifi_device_legacy = {
.probe = wifi_probe
.remove = wifi_remove
.suspend = wifi_supend
.resume = wifi_resume
.driver = {
.name = “bcm4329_wlan”
}
}
25
上面的展示了wifi平台设备驱动的注册过程,那么在平台相关的代码区应该有wifi作为平台设备被初始化和注册的地方:
Path: kernel/arch/arm/mach-msm/msm_
static struct resource mahimahi_wifi_resources[] = {
[0] = {
.name = "bcm4329_wlan_irq",
.start =
MSM_GPIO_TO_INT(MAHIMAHI_GPIO_WIFI_IRQ),
.end =
MSM_GPIO_TO_INT(MAHIMAHI_GPIO_WIFI_IRQ),
.flags = IORESOURCE_IRQ |
IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE,
},
};
static struct wifi_platform_data mahimahi_wifi_control = {
.set_power = mahimahi_wifi_power,
.set_reset = mahimahi_wifi_reset,
.set_carddetect = mahimahi_wifi_set_carddetect,
.mem_prealloc = mahimahi_wifi_mem_prealloc,
};
static struct platform_device mahimahi_wifi_device = {
.name = "bcm4329_wlan",
.id = 1,
.num_resources = ARRAY_SIZE(mahimahi_wifi_resources),
.resource = mahimahi_wifi_resources,
.dev = {
.platform_data = &mahimahi_wifi_control,
},
};
上面是对wifi_device设备的初始化,下面是对该设备的注册:
static int __init mahimahi_wifi_init(void)
{
int ret;
if (!machine_is_mahimahi())
return 0;
printk("%s: startn", __func__);
mahimahi_wifi_update_nvs("sd_oobonly=1rn", 0);
mahimahi_wifi_update_nvs("btc_params70=0x32rn", 1);
26
mahimahi_init_wifi_mem();
ret = platform_device_register(&mahimahi_wifi_device);
return ret;
}
late_initcall(mahimahi_wifi_init); //表明在系统启动的后期会自动调用加载该模块
这样,通过上面的初始化和注册流程,wifi设备作为平台设备和驱动就可以握手成功了,这里的平台驱动只是对wifi设备的简单管理,如对wifi设备的挂起和恢复等操作了。但是在wifi设备初始化之前是不能够被挂起和恢复的,那么wifi设备是如何初始化的呢?
Path: wl/sys/wl_android.c
static int wifi_probe(struct platform_device *pdev)
{
struct wifi_platform_data *wifi_ctrl =
(struct wifi_platform_data *)(pdev->rm_data);
DHD_ERROR(("## %sn", __FUNCTION__));
wifi_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
"bcmdhd_wlan_irq");
if (wifi_irqres == NULL)
wifi_irqres = platform_get_resource_byname(pdev,
IORESOURCE_IRQ, "bcm4329_wlan_irq");
wifi_control_data = wifi_ctrl;
wifi_set_power(1, 0); /* Power On */
wifi_set_carddetect(1); /* CardDetect (0->1) */
up(&wifi_control_sem);
return 0;
}
这是wifi平台设备驱动注册时成功匹配wifi设备后调用的函数wifi_probe(),它的主要工作就是从wifi设备中获取终端资源,并获取wifi_platform_data类型结构赋予wifi_control_data变量,这一步很重要,下面就可以看出了它的重要性。然后调用wifi_set_power和wifi_set_carddetect函数给wifi芯片上电并检测。
int wifi_set_power(int on, unsigned long msec)
{
DHD_ERROR(("%s = %dn", __FUNCTION__, on));
if (wifi_control_data && wifi_control_data->set_power) {
wifi_control_data->set_power(on);
}
27
if (msec)
msleep(msec);
return 0;
}
Wifi_set_power函数中调用wifi_control_data->set_power(on),wifi_control_data就是刚才说的那个重要变量,注意它是从wifi_device平台设备的wifi_platform_data获取的,那么看看上面的wifi_device初始化的代码:
static struct platform_device mahimahi_wifi_device = {
.name = "bcm4329_wlan",
.id = 1,
.num_resources = ARRAY_SIZE(mahimahi_wifi_resources),
.resource = mahimahi_wifi_resources,
.dev = {
.platform_data = &mahimahi_wifi_control,
},
};
static struct wifi_platform_data mahimahi_wifi_control = {
.set_power = mahimahi_wifi_power,
.set_reset = mahimahi_wifi_reset,
.set_carddetect = mahimahi_wifi_set_carddetect,
.mem_prealloc = mahimahi_wifi_mem_prealloc,
};
所以它实际调用的是mahimahi_wifi_power函数,该函数的定义在kernel/arch/arm
/mach-msm/board-mahimahi-mmc.c之中:
int mahimahi_wifi_power(int on)
{
printk("%s: %dn", __func__, on);
if (on) {
config_gpio_table(wifi_on_gpio_table,
ARRAY_SIZE(wifi_on_gpio_table));
mdelay(50);
} else {
config_gpio_table(wifi_off_gpio_table,
ARRAY_SIZE(wifi_off_gpio_table));
}
mdelay(100);
gpio_set_value(MAHIMAHI_GPIO_WIFI_SHUTDOWN_N, on);
WIFI_SHUTDOWN */
mdelay(200);
/*
28
mahimahi_wifi_power_state = on;
return 0;
}
调用gpio_set_value操作wifi芯片,给wifi芯片上电。那么来看看wifi_set_
carddetect函数究竟干了什么:
Path:wl/sys/wl_android.c
static int wifi_set_carddetect(int on)
{
DHD_ERROR(("%s = %dn", __FUNCTION__, on));
if (wifi_control_data && wifi_control_data->set_carddetect) {
wifi_control_data->set_carddetect(on);
}
return 0;
}
同样会调用wifi_device的mahimahi_wifi_set_carddetect函数:
Path: kernel/arch/arm/mach-msm/board-mahimahi-mmc.c
int mahimahi_wifi_set_carddetect(int val)
{
pr_info("%s: %dn", __func__, val);
mahimahi_wifi_cd = val;
if (wifi_status_cb) {
wifi_status_cb(val, wifi_status_cb_devid);
} else
pr_warning("%s: Nobody to notifyn", __func__);
return 0;
}
Wifi_status_cb代码:
static int mahimahi_wifi_status_register(
void (*callback)(int card_present, void *dev_id),
void *dev_id)
{
if (wifi_status_cb)
return -EAGAIN;
wifi_status_cb = callback;
wifi_status_cb_devid = dev_id;
return 0;
}
static unsigned int mahimahi_wifi_status(struct device *dev)
{
29
return mahimahi_wifi_cd;
}
static struct mmc_platform_data mahimahi_wifi_data = {
.ocr_mask = MMC_VDD_28_29,
.built_in = 1,
.status = mahimahi_wifi_status,
.register_status_notify = mahimahi_wifi_status_register,
.embedded_sdio = &mahimahi_wifi_emb_data,
};
由上面代码;不难看出会有个地方调用mahimahi_wifi_status_register设置wifi_status_cb这个回调函数,可以跟踪这个mahimahi_wifi_data结构体,来看看它被传递给了谁:
int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat,
unsigned int stat_irq, unsigned long stat_irq_flags);
int __init mahimahi_init_mmc(unsigned int sys_rev, unsigned debug_uart)
{
……
msm_add_sdcc(1, &mahimahi_wifi_data, 0, 0);
……
if (system_rev > 0)
msm_add_sdcc(2, &mahimahi_sdslot_data, 0, 0);
else {
mahimahi_sdslot_ = mahimahi_sdslot_status_rev0;
mahimahi_sdslot_er_status_notify = NULL;
set_irq_wake(MSM_GPIO_TO_INT(MAHIMAHI_GPIO_SDMC_CD_REV0_N), 1);
msm_add_sdcc(2, &mahimahi_sdslot_data,
……
}
可以跟踪到这里Path:kernel/arch/arm/mach-msm/devices-msm7x30.c
struct platform_device msm_device_sdc1 = {
.name = "msm_sdcc",
.id = 1,
.num_resources = ARRAY_SIZE(resources_sdc1),
.resource = resources_sdc1,
.dev = {
.coherent_dma_mask = 0xffffffff,
},
30
};
struct platform_device msm_device_sdc2 = {
.name = "msm_sdcc",
.id = 2,
.num_resources = ARRAY_SIZE(resources_sdc2),
.resource = resources_sdc2,
.dev = {
.coherent_dma_mask = 0xffffffff,
},
};
struct platform_device msm_device_sdc3 = {
.name = "msm_sdcc",
.id = 3,
.num_resources = ARRAY_SIZE(resources_sdc3),
.resource = resources_sdc3,
.dev = {
.coherent_dma_mask = 0xffffffff,
},
};
struct platform_device msm_device_sdc4 = {
.name = "msm_sdcc",
.id = 4,
.num_resources = ARRAY_SIZE(resources_sdc4),
.resource = resources_sdc4,
.dev = {
439,2-16 62%
.coherent_dma_mask = 0xffffffff,
},
};
static struct platform_device *msm_sdcc_devices[] __initdata = {
&msm_device_sdc1,
&msm_device_sdc2,
&msm_device_sdc3,
&msm_device_sdc4,
};
int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat,
unsigned int stat_irq, unsigned long stat_irq_flags)
{
31
……
pdev = msm_sdcc_devices[controller-1]; //因为传过来的controller是1,所以下面注册的是第一个平台设备
pdev->rm_data = plat; //被传递给平台设备的platform_data
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "status_irq");
if (!res)
return -EINVAL;
else if (stat_irq) {
res->start = res->end = stat_irq;
res->flags &= ~IORESOURCE_DISABLED;
res->flags |= stat_irq_flags;
}
return platform_device_register(pdev); //如上所述
}
那么这个平台设备是什么呢,就是sd卡控制器,也就是前面说的host驱动所驱动的主机控制设备。
Path: drivers/mmc/host/msm_sdcc.c
static struct platform_driver msmsdcc_driver = {
.probe = msmsdcc_probe,
.suspend = msmsdcc_suspend,
.resume = msmsdcc_resume,
.driver = {
.name = "msm_sdcc",
},
};
static int __init msmsdcc_init(void)
{
return platform_driver_register(&msmsdcc_driver);
}
驱动成功匹配设备后,调用probe函数:
static int
msmsdcc_probe(struct platform_device *pdev)
{
......
if (stat_irqres && !(stat_irqres->flags & IORESOURCE_DISABLED)) {
……
} else if (plat->register_status_notify) {
plat->register_status_notify(msmsdcc_status_notify_cb, host);
} else if (!plat->status)
32
......
}
msmsdcc_status_notify_cb调用msmsdcc_check_status函数:
msmsdcc_status_notify_cb(int card_present, void *dev_id)
{
struct msmsdcc_host *host = dev_id;
printk(KERN_DEBUG "%s: card_present %dn", mmc_hostname(host->mmc),
card_present);
msmsdcc_check_status((unsigned long) host);
}
msmsdcc_check_status调用mmc_detect_change函数:
static void
msmsdcc_check_status(unsigned long data)
{
……
if (status ^ host->oldstat) {
pr_info("%s: Slot status change detected (%d -> %d)n",
mmc_hostname(host->mmc), host->oldstat, status);
if (status && !host->plat->built_in)
mmc_detect_change(host->mmc, (5 * HZ) / 2);
else
mmc_detect_change(host->mmc, 0);
}
host->oldstat = status;
out:
if (host->on)
mod_timer(&host->timer, jiffies + HZ);
}
可以看到mmc_detect_change被调用了,这个函数触发了一个延时工作:
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
……
mmc_schedule_delayed_work(&host->detect, delay);
}
这个时候它会在delay时间后,执行host->detect延时工作对应的函数,在host驱动注册并匹配设备成功后执行的probe函数里,会调用mmc_alloc_host动态创建一个mmc_host:
33
msmsdcc_probe(struct platform_device *pdev)
{
......
/*
* Setup our host structure
*/
mmc = mmc_alloc_host(sizeof(struct msmsdcc_host), &pdev->dev);
if (!mmc) {
ret = -ENOMEM;
goto out;
}
......
}
mmc_alloc_host初始化工作入口:
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
......
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
......
}
mmc_rescan是core.c中一个很重要的函数,它遵照 SDIO 卡协议的 SDIO 卡启动过程,包括了非激活模式、卡识别模式和数据传输模式三种模式共九种状态的转换,你需要参照相关规范来理解。
void mmc_rescan(struct work_struct *work)
{
struct mmc_host *host =
container_of(work, struct mmc_host, );
......
mmc_power_up(host);
sdio_reset(host);
mmc_go_idle(host);
mmc_send_if_cond(host, host->ocr_avail);
/*
* First we search
*/
err = mmc_send_io_op_cond(host, 0, &ocr);
if (!err) {
if (mmc_attach_sdio(host, ocr))
mmc_power_off(host);
34
extend_wakelock = 1;
goto out;
}
......
}
这个mmc_attach_sdio函数很重要,它是SDIO卡的初始化的起点,主要工作包括:匹配SDIO卡的工作电压,分配并初始化mmc_card结构,然后注册mmc_card到系统中:
/*
* Starting point for SDIO card init.
*/
int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
{
……
mmc_attach_bus(host, &mmc_sdio_ops); //初始化host的bus_ops
……
host->ocr = mmc_select_voltage(host, ocr); //匹配SDIO卡工作电压
……
/*
* Detect and init the card.
*/
err = mmc_sdio_init_card(host, host->ocr, NULL, 0); //检测,分配初始化mmc_card
if (err)
goto err;
card = host->card;
/*
* If needed, disconnect card detection pull-up resistor.
*/
err = sdio_disable_cd(card);
if (err)
goto remove;
/*
* Initialize (but don't add) all present functions.
*/
for (i = 0; i < funcs; i++, card->sdio_funcs++) {
#ifdef CONFIG_MMC_EMBEDDED_SDIO
35
if (host->embedded_sdio_) {
struct sdio_func *tmp;
tmp = sdio_alloc_func(host->card);
if (IS_ERR(tmp))
goto remove;
tmp->num = (i + 1);
card->sdio_func[i] = tmp;
tmp->class = host->embedded_sdio_[i].f_class;
tmp->max_blksize =
host->embedded_sdio_[i].f_maxblksize;
tmp->vendor = card->;
tmp->device = card->;
} else {
#endif
err = sdio_init_func(host->card, i + 1);
if (err)
goto remove;
#ifdef CONFIG_MMC_EMBEDDED_SDIO
}
#endif
}
mmc_release_host(host);
/*
* First add the card to the
*/
err = mmc_add_card(host->card); //添加mmc_card
if (err)
goto remove_added;
/*
* ...then the SDIO functions.
*/
for (i = 0;i < funcs;i++) {
err = sdio_add_func(host->card->sdio_func[i]); //将sdio_func加入系统
if (err)
goto remove_added;
}
return 0;
......
36
}
这样,SDIO卡已经初始化成功并添加到了驱动中。上面说的过程是在SDIO设备注册时的调用流程,mmc_rescan是整个流程主体部分,由它来完成SDIO设备的初始化和添加。其实上面的流程只是创建,初始化,添加SDIO设备的一条线,还有另外的两条线也会调用mmc_rescan函数进行SDIO设备的上述操作:
(1) 加载SDIO host驱动模块
(2) SDIO设备中断
8.3.2.1 加载SDIO host驱动模块
Host作为平台设备被注册,前面也有列出相应源码:
static struct platform_driver msmsdcc_driver = {
.probe = msmsdcc_probe,
.suspend = msmsdcc_suspend,
.resume = msmsdcc_resume,
.driver = {
.name = "msm_sdcc",
},
};
static int __init msmsdcc_init(void)
{
return platform_driver_register(&msmsdcc_driver);
}
Probe函数会调用mmc_alloc_host函数(代码前面已经贴出)来创建mmc_host结构变量,进行必要的初始化之后,调用mmc_add_host函数将它添加到驱动里面:
int mmc_add_host(struct mmc_host *host)
{
……
err = device_add(&host->class_dev);
if (err)
return err;
mmc_start_host(host);
if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
register_pm_notifier(&host->pm_notify);
return 0;
37
}
Mmc_start_host定义如下:
void mmc_start_host(struct mmc_host *host)
{
mmc_power_off(host);
mmc_detect_change(host, 0);
}
mmc_power_off 中对 ios 进行了设置,然后调用 mmc_set_ios(host);
host->_mode = MMC_POWER_OFF;
host->_width = MMC_BUS_WIDTH_1;
host-> = MMC_TIMING_LEGACY;
mmc_set_ios(host);
mmc_set_ios(host) 中的关键语句 host->ops->set_ios(host, ios),实际上在host驱动的probe函数中就已经对host->ops进行了初始化:
……
/*
* Setup MMC host structure
*/
mmc->ops = &msmsdcc_ops;
……
static const struct mmc_host_ops msmsdcc_ops = {
.request = msmsdcc_request,
.set_ios = msmsdcc_set_ios,
.enable_sdio_irq = msmsdcc_enable_sdio_irq,
};
所以实际上调用的是msmsdcc_set_ios,关于这个函数就不介绍了,可以参考源码,再看 mmc_detect_change(host, 0),最后一句是:
mmc_schedule_delayed_work(&host->detect, delay);
实际上就是调用我们前面说的延时函数 mmc_rescan,后面的流程是一样的。
8.3.2.2 SDIO设备中断
SDIO设备通过SDIO总线与host相连,SDIO总线的DAT[1]即pin8可以作为中断线使用,当SDIO设备向host产生中断时,host会对终端做出相应的动作,在host驱动的probe函数中申请并注册相应的中断函数:
static int
msmsdcc_probe(struct platform_device *pdev)
{
38
......
cmd_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
"cmd_irq");
pio_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
"pio_irq");
stat_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
"status_irq");
......
if (stat_irqres && !(stat_irqres->flags & IORESOURCE_DISABLED)) {
unsigned long irqflags = IRQF_SHARED |
(stat_irqres->flags & IRQF_TRIGGER_MASK);
host->stat_irq = stat_irqres->start;
ret = request_irq(host->stat_irq,
msmsdcc_platform_status_irq,
irqflags,
DRIVER_NAME " (slot)",
host);
if (ret) {
pr_err("%s: Unable to get slot IRQ %d (%d)n",
mmc_hostname(mmc), host->stat_irq, ret);
goto clk_disable;
}
}
......
}
当产生相应的中断时调用msmsdcc_platform_status_irq中断处理函数,这个函数的处理流程:
msmsdcc_platform_status_irq—>
msmsdcc_check_status
mmc_detect_change
mmc_rescan
那么,这里为何调用mmc_rescan呢?因为前面说过mmc_rescanrescan函数主要用于SDIO设备的初始化,如果SDIO设备产生中断不应该是已经初始化可以使用了吗?其实mmc_rescan还有其它的工作,从函数名就能看出来它还有再扫描检测功能,即如果设备产生了中断,mmc_rescan函数一开始就会再次检测所有挂接在该host上的所有SDIO设备,确认是否存在,如果不存在就做相应的释放工作,以确保数据的一致性。如果检测到了新的设备那么它就会创建一个新的mmc_card,初始化并添加该设备。
39
中断引发的调用mmc_rescan动作的意义:实现了SDIO设备的热插拔功能。
8.3.3 WIFI驱动流程(二)
dhd_mudule_init
Dhd_bus_register
Bcmsdh_register
Sdio_function_init
Sdio_register_driver
Bcmsdh_sdmmc_probe
Bcmsdh_probe
dhdsdio_probe
此调用流程由dhd_bus_register发起,通过sdio_register_driver注册一个sdio设备驱动,然后通过dhdsdio_probe初始化并注册一个网络设备,网络设备的注册标志着wifi驱动已经成功加载,关于网络设备的创建,初始化和注册后面会有详细介绍,先来理一下上面的调用流程,:
dhd_mudule_init—>
Dhd_bus_register
//path: dhd/sys/dhd_linux.c
// dhd/sys/dhd_sdio.c
40
Bcmsdh_register // bcmsdio/sys/bcmsdh_linux.c
Sdio_function_init // bcmsdio/sys/bcmsdh_sdmmc_linux.c
Sdio_register_driver // bcmsdio/sys/bcmsdh_sdmmc_linux.c
Bcmsdh_sdmmc_probe//bcmsdio/sys/bcmsdh_sdmmc_linux.c
Bcmsdh_probe //bcmsdio/sys/bcmsdh_linux.c
Bcmsdio_probe //dhd/sys/dhd_sdio.c
这里注意上面两个红色标记的函数,sdio_register_driver注册了一个sdio设备,在匹配成功后调用bcmsdh_sdmmc_probe函数,这个函数会调用bcmsdh_probe。这里有一点要注意:浏览bcmsdh_linux.c文件可以看出,在bcmsdh_register函数中,当定义了BCMLXSDMMC宏时,会调用sdio_function_init函数,否则调用driver_register函数:
int
bcmsdh_register(bcmsdh_driver_t *driver)
{
int error = 0;
drvinfo = *driver; //注意这里,后面会介绍到它的用处
#if defined(BCMPLATFORM_BUS)
#if defined(BCMLXSDMMC)
SDLX_MSG(("Linux Kernel SDIO/MMC Drivern"));
error = sdio_function_init();
#else
SDLX_MSG(("Intel PXA270 SDIO Drivern"));
error = driver_register(&bcmsdh_driver);
#endif /* defined(BCMLXSDMMC) */
return error;
#endif /* defined(BCMPLATFORM_BUS) */
#if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC)
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
if (!(error = pci_module_init(&bcmsdh_pci_driver)))
return 0;
#else
if (!(error = pci_register_driver(&bcmsdh_pci_driver)))
return 0;
#endif
SDLX_MSG(("%s: pci_module_init failed 0x%xn", __FUNCTION__, error));
#endif /* BCMPLATFORM_BUS */
41
return error;
}
上面的流程中有sdio_function_init的调用出现,所以这里实际上BCMLXSDMMC宏被定义了,bcmsdh_probe函数只是作为一个普通函数被调用,如果不定义该宏,那么bcmsdh_probe函数会被作为驱动匹配设备后第一个调用的函数而被自动调用。
再看看dhdsdio_probe函数调用的玄机,从上面的bcmsdh_register函数可以看出它的参数被传递给了drvinfo,看看bcmsdh_register的调用地方:
static bcmsdh_driver_t dhd_sdio = {
dhdsdio_probe,
dhdsdio_disconnect
};
int
dhd_bus_register(void)
{
DHD_TRACE(("%s: Entern", __FUNCTION__));
return bcmsdh_register(&dhd_sdio);
}
上面传递的参数是dhd_sdio结构变量,被用两个函数初始化了,那么哪一个是attach呢?需要找到定义bcmsdh_driver_t结构定义的地方:
Path:src/include/bcmsdh.h
/* callback functions */
typedef struct {
/* attach to device */
void *(*attach)(uint16 vend_id, uint16 dev_id, uint16 bus, uint16 slot,
uint16 func, uint bustype, void * regsva, osl_t * osh,
void * param);
/* detach from device */
void (*detach)(void *ch);
} bcmsdh_driver_t;
没错,就是第一个dhdsdio_probe函数,再来看看什么地方调用了这个attach函数:
Path: bcmsdio/sys/bcmsdh_linux.c
#ifndef BCMLXSDMMC
static
#endif /* BCMLXSDMMC */
42
int bcmsdh_probe(struct device *dev)
{
......
if (!(sdhc->ch = ((vendevid >> 16),
(vendevid & 0xFFFF), 0, 0, 0, 0,
(void *)regs, NULL, sdh))) {
SDLX_MSG(("%s: device attach failedn", __FUNCTION__));
goto err;
}
return 0;
......
}
红色部分的函数调用是,就是上面传递过来的dhdsdio_probe函数了,仔细阅读你会发现上面那个bcmsdh_driver_t结构体定义的地方有个说明,即把该结构的成员函数当做callback函数来使用,这就是它的用意所在。
8.3.4 网络设备注册流程
Dhdsdio_probe
Dhd_attach Dhd_net_attach
alloc_etherdev register_netdev
Dhd_add_if
上面是网络设备注册流程,在dhdsdio_probe函数中先后对dhd_attach和dhd_net_attach两个函数调用,dhd_attach主要用于创建和初始化dhd_info_t和net_device两个结构变量,然后调用dhd_add_if将创建的net_device变量添加到dhd_info_t变量的iflist列表中(支持多接口)。
43
Dhd_attach的流程如下:
dhd_pub_t *
dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
{
dhd_info_t *dhd = NULL;
struct net_device *net = NULL;
......
/* Allocate etherdev, including space for private structure */
if (!(net = alloc_etherdev(sizeof(dhd)))) { //网络设备的创建
DHD_ERROR(("%s: OOM - alloc_etherdevn", __FUNCTION__));
goto fail;
}
dhd_state |= DHD_ATTACH_STATE_NET_ALLOC;
/* Allocate primary dhd_info */
if (!(dhd = MALLOC(osh, sizeof(dhd_info_t)))) { //dhd的创建
DHD_ERROR(("%s: OOM - alloc dhd_infon", __FUNCTION__));
goto fail;
}
......
/* Set network interface name if it was provided as module parameter */
if (iface_name[0]) {
int len;
char ch;
strncpy(net->name, iface_name, IFNAMSIZ);
net->name[IFNAMSIZ - 1] = 0;
len = strlen(net->name);
ch = net->name[len - 1];
if ((ch > '9' || ch < '0') && (len < IFNAMSIZ - 2))
strcat(net->name, "%d");
}
if (dhd_add_if(dhd, 0, (void *)net, net->name, NULL, 0, 0) ==
DHD_BAD_IF) //将前面创建的net添加到iflist列表中
goto fail;
dhd_state |= DHD_ATTACH_STATE_ADD_IF;
......
Memcpy(netdev_priv(net), &dhd, sizeof(dhd)); //关联dhd和net
//dhd的初始化工作
}
44
Dhd_add_if的添加网络接口流程:
int
dhd_add_if(dhd_info_t *dhd, int ifidx, void *handle, char *name,
uint8 *mac_addr, uint32 flags, uint8 bssidx)
{
dhd_if_t *ifp;
DHD_TRACE(("%s: idx %d, handle->%pn", __FUNCTION__, ifidx,
handle));
ASSERT(dhd && (ifidx < DHD_MAX_IFS));
ifp = dhd->iflist[ifidx];
if (ifp != NULL) {
if (ifp->net != NULL) {
netif_stop_queue(ifp->net);
unregister_netdev(ifp->net);
free_netdev(ifp->net); //如果已经存在,释放net成员
}
} else
if ((ifp = MALLOC(dhd->, sizeof(dhd_if_t))) == NULL) {
DHD_ERROR(("%s: OOM - dhd_if_tn",
__FUNCTION__)); //否则,创建一个dhd_if_t结构变量
return -ENOMEM;
}
memset(ifp, 0, sizeof(dhd_if_t));
ifp->info = dhd; //进行系列初始化,添加工作
dhd->iflist[ifidx] = ifp;
strncpy(ifp->name, name, IFNAMSIZ);
ifp->name[IFNAMSIZ] = '0';
if (mac_addr != NULL)
memcpy(&ifp->mac_addr, mac_addr, ETHER_ADDR_LEN);
if (handle == NULL) {
ifp->state = DHD_IF_ADD;
ifp->idx = ifidx;
ifp->bssidx = bssidx;
ASSERT(&dhd->thr_sysioc__pid >= 0);
up(&dhd->thr_sysioc_);
} else
ifp->net = (struct net_device *)handle; //handle即一个net_device变量
45
return 0;
}
这样,一个net_device网路设备就被添加到了接口管理列表中了,但是这是网路设备还没有完成初始化和注册工作,bcmsdio_probe函数随后对dhd_net_attach的调用完成了这个操作:
int
dhd_net_attach(dhd_pub_t *dhdp, int ifidx)
{
dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
struct net_device *net = NULL;
int err = 0;
uint8 temp_addr[ETHER_ADDR_LEN] = { 0x00, 0x90, 0x4c, 0x11, 0x22,
0x33 };
DHD_TRACE(("%s: ifidx %dn", __FUNCTION__, ifidx));
ASSERT(dhd && dhd->iflist[ifidx]);
net = dhd->iflist[ifidx]->net; //首先从刚才添加的接口列表中取出net,然后进行下面的系列初始化工作
ASSERT(net);
//根据内核版本信息,选择对net成员函数的初始化方式,假设是2.6.30的版本
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31))
ASSERT(!net->open);
net->get_stats = dhd_get_stats;
net->do_ioctl = dhd_ioctl_entry;
net->hard_start_xmit = dhd_start_xmit;
net->set_mac_address = dhd_set_mac_address;
net->set_multicast_list = dhd_set_multicast_list;
net->open = net->stop = NULL;
#else
ASSERT(!net->netdev_ops);
net->netdev_ops = &dhd_ops_virt;
#endif
/* Ok, link into the */
if (ifidx == 0) {
/*
* device functions for the primary interface only
*/
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31))
net->open = dhd_open;
net->stop = dhd_stop;
46
#else
net->netdev_ops = &dhd_ops_pri;
#endif
} else {
/*
* We have to use the primary MAC for virtual interfaces
3417,1-8 66%
*/
memcpy(temp_addr, dhd->iflist[ifidx]->mac_addr,
ETHER_ADDR_LEN);
/*
* Android sets the locally administered bit to indicate that this is a
* portable hotspot. This will not work in simultaneous AP/STA
mode,
* nor with P2P. Need to set the Donlge's MAC address, and then
use that.
*/
if (!memcmp(temp_addr, dhd->iflist[0]->mac_addr,
ETHER_ADDR_LEN)) {
DHD_ERROR(("%s interface [%s]: set locally
administered bit in MACn",
__func__, net->name));
temp_addr[0] |= 0x02;
}
}
net->hard_header_len = ETH_HLEN + dhd->;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
net->ethtool_ops = &dhd_ethtool_ops;
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */
#if defined(CONFIG_WIRELESS_EXT)
#if WIRELESS_EXT < 19
net->get_wireless_stats = dhd_get_wireless_stats;
#endif /* WIRELESS_EXT < 19 */
#if WIRELESS_EXT > 12
net->wireless_handlers = (struct iw_handler_def *)&wl_iw_handler_def; //这里的初始化工作很重要,之后的ioctl流程会涉及到对它的使用
#endif /* WIRELESS_EXT > 12 */
#endif /* defined(CONFIG_WIRELESS_EXT) */
dhd-> = DBUS_RX_BUFFER_SIZE_DHD(net);
//设置设备地址
47
memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN);
if ((err = register_netdev(net)) != 0) { //注册net
DHD_ERROR(("couldn't register the net device, err %dn", err));
goto fail;
}
……
}
到这里net网络设备就被注册到系统中了,设备准备好了就好对设备进行访问了
9 IOCTL的调用逻辑
之所以要分析这个,是因为上层wpa_supplicant和WIFI驱动打交道的方式,多半是通过ioctl的方式进行的,所以看看它的调用逻辑(这里只列出其主要的调用逻辑):
48
Sock_ioctl
Dev_ioctl
wext_handle_ioctl
User ioctl entry
wext_ioctl_dispatch
Wireless_process_ioctl
Get_handler
1
Ioctl_Standard/private_call
2
dev->netdev_ops->ndo_do_ioctl
Handler[i] Dhd_ioctl
上面便是用户ioctl调用的流程图,它最终分为两条线即有两种支持,选择那一条或两条都选(个人感觉最好选第2条线,因为它最后也是会调用到相应的函数的,而且还有其它更多的命令支持),从实际的代码来看,如果dev->netdev_ops
->ndo_do_ioctl被初始化了,那么它一定会被调用,是否被初始化,在前面选择对net结构变量的初始化方式中有讨论过。
下面来具体看看该调用流程,首先说明下,上面的流程主要实现在kernel/net/wireless/wext_core.c文件中,这是wireless的协议层实现,恰好我们在wpa_supplicant中通常选择的驱动类型也是wext,它的入口函数是
49


发布评论