Android Wifi work station Framework and Architecture
with wpa_supplicant 0.8.X, BCM4329.
转载请注明出处。
WifiSettings是主对话框
167
168 @Override
169 public void onActivityCreated(Bundle savedInstanceState) {
170 // We don't call super.onActivityCreated() here, since it assumes we already set up
171 // Preference (probably in onCreate()), while WifiSettings exceptionally set it up in
172 // this method.
173
174 mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
175 mWifiManager.asyncConnect(getActivity(), new WifiServiceHandler());
176 if (savedInstanceState != null
177 && savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) {
178 mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE);
179 mAccessPointSavedState = savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
180 }
1101 public void asyncConnect(Context srcContext, Handler srcHandler) {
1102 mAsyncChannel.connect(srcContext, srcHandler, getMessenger());
1103 }
establish a half connect between WifiManager and WifiService.
1117 public void connectNetwork(WifiConfiguration config) {
1118 if (config == null) {
1119 return;
1120 }
1121 mAsyncChannel.sendMessage(CMD_CONNECT_NETWORK, config);
1122 }
Activity创建的时候会建立和WifiService的AsyncChannel的连接,WifiService同样会建立一个AsyncChannelHandler来处理来自这个Channel的命令消息。AsyncChannel的所谓异步主要用来传递比较耗时的操作并把结果返回给原请求者。AsyncChannel两端分别有一个MessageHandler,srcHandler请求destHandler,destHandler把结果发回给srcHandler,主要是用于处理比较耗时的操作且在WifiManager中处理返回结果。
严格来说,在WifiService的Interface方法之外再做出一个通道提供额外的方法并不是一个什么好的设计。命令是可以通过同步接口发送给WifiService的,但是WifiService的处理结果如果通过broadcast或intent给WifiManager,则又解耦的过度;如果WifiManager实现一个binder做event sink,又有点小题大做,所以这儿引入这么个AsyncChannel实在是不得以而为之。
WifiSettings界面使能Wifi时会使用定时器请求扫描,获取扫描结果,列出AP列表。Scanner使用定时器,周期性向WifiService请求主动扫描,定时原理是发出本次扫描请求后延迟1s发送下次请求。相关代码如下:
private class Scanner extends Handler {
private int mRetry = 0;
void resume() {
if (!hasMessages(0)) {
sendEmptyMessage(0);
}
}
void forceScan() {
removeMessages(0);
sendEmptyMessage(0);
}
void pause() {
mRetry = 0;
removeMessages(0);
}
@Override
public void handleMessage(Message message) {
if (mWifiManager.startScanActive()) {
mRetry = 0;
} else if (++mRetry >= 3) {
mRetry = 0;
Toast.makeText(getActivity(), R.string.wifi_fail_to_scan,
Toast.LENGTH_LONG).show();
return;
}
sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS);
}
}
列出AP列表的代码如下:
In WifiSettings.java
final List<ScanResult> results = mWifiManager.getScanResults(); //同步操作
if (results != null) {
for (ScanResult result : results) {
// Ignore hidden and ad-hoc networks.
if (result.SSID == null || result.SSID.length() == 0 || result.capabilities.contains("[IBSS]")) {
continue;
}
boolean found = false;
for (AccessPoint accessPoint : apMap.getAll(result.SSID)) {
if (accessPoint.update(result))
found = true;
}
if (!found) {
AccessPoint accessPoint = new AccessPoint(getActivity(), result);
accessPoints.add(accessPoint);
apMap.put(accessPoint.ssid, accessPoint);
}
}
}
private void updateAccessPoints() {
final int wifiState = mWifiManager.getWifiState();
switch (wifiState) {
case WifiManager.WIFI_STATE_ENABLED:
// AccessPoints are automatically sorted with TreeSet.
final Collection<AccessPoint> accessPoints = constructAccessPoints();
getPreferenceScreen().removeAll();
if (mInXlSetupWizard) {
((WifiSettingsForSetupWizardXL)getActivity()).onAccessPointsUpdated(
getPreferenceScreen(), accessPoints);
} else {
for (AccessPoint accessPoint : accessPoints) {
// When WAPI is not customized to be on all
// WAPI APs will be invisible
if (accessPoint.isVisible()) {
getPreferenceScreen().addPreference(accessPoint);
}
}
}
break;
case WifiManager.WIFI_STATE_ENABLING:
getPreferenceScreen().removeAll();
break;
case WifiManager.WIFI_STATE_DISABLING:
addMessagePreference(R.string.wifi_stopping);
break;
case WifiManager.WIFI_STATE_DISABLED:
addMessagePreference(R.string.wifi_empty_list_wifi_off);
break;
}
}
可以看出对ADHOC的AP,隐藏AP(无SSID的)做了过滤处理。
当在AP列表上,长按某个AP时弹出三菜单Menu - ContextMenu。
这个Menu主要是连接该AP,修改该AP,忘记该AP,其处理代码如下:
@Override
public boolean onContextItemSelected(MenuItem item) {
if (mSelectedAccessPoint == null) {
return super.onContextItemSelected(item);
}
switch (item.getItemId()) {
case MENU_ID_CONNECT: {
if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
if (!requireKeyStore(mSelectedAccessPoint.getConfig())) {
mWifiManager.connectNetwork(mSelectedAccessPoint.networkId);
}
} else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) {
/** Bypass dialog for unsecured networks */
mSelectedAccessPoint.generateOpenNetworkConfig();
mWifiManager.connectNetwork(mSelectedAccessPoint.getConfig());
} else {
showConfigUi(mSelectedAccessPoint, true);
}
return true;
}
case MENU_ID_FORGET: {
mWifiManager.forgetNetwork(mSelectedAccessPoint.networkId);
return true;
}
case MENU_ID_MODIFY: {
showConfigUi(mSelectedAccessPoint, true);
return true;
}
}
return super.onContextItemSelected(item);
}
可以看出如果该AP已经经过配置,那么直接连接,如果没有经过配置且该AP没有密码,那么直接连接,否则则弹出配置对话框先进行配置(选择加密类型和输入密码)。
当点击AP列表中的某个AP时,直接根据该AP的情况进行处理,代码如下:
387 @Override
388 public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
389 if (preference instanceof AccessPoint) {
390 mSelectedAccessPoint = (AccessPoint) preference;
391 /** Bypass dialog for unsecured, unsaved networks */
392 if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE &&
393 mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
394 mSelectedAccessPoint.generateOpenNetworkConfig();
395 mWifiManager.connectNetwork(mSelectedAccessPoint.getConfig());
396 } else {
397 showConfigUi(mSelectedAccessPoint, false);
398 }
399 } else {
400 return super.onPreferenceTreeClick(screen, preference);
401 }
402 return true;
403 }
可以看出和长按菜单的处理逻辑相似。
当使用Setttings主界面或Settings/Wifi界面的Switcher开关Wifi时,处理代码会调用到mWifiEnabler的setSwitch方法,然后会通知其listener,涉及方法如下:
88 public void setSwitch(Switch switch_) {
89 if (mSwitch == switch_) return;
90 mSwitch.setOnCheckedChangeListener(null);
91 mSwitch = switch_;
92 mSwitch.setOnCheckedChangeListener(this);
93
94 final int wifiState = mWifiManager.getWifiState();
95 boolean isEnabled = wifiState == WifiManager.WIFI_STATE_ENABLED;
96 boolean isDisabled = wifiState == WifiManager.WIFI_STATE_DISABLED;
97 mSwitch.setChecked(isEnabled);
98 mSwitch.setEnabled(isEnabled || isDisabled);
99 }
100
101 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
102 //Do nothing if called as a result of a state machine event
103 if (mStateMachineEvent) {
104 return;
105 }
106 // Show toast message if Wi-Fi is not allowed in airplane mode
107 if (isChecked && !WirelessSettings.isRadioAllowed(mContext, Settings.System.RADIO_WIFI)) {
108 Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();
109 // Reset switch to off. No infinite check/listenenr loop.
110 buttonView.setChecked(false);
111 }
112
113 // Disable tethering if enabling Wifi
114 int wifiApState = mWifiManager.getWifiApState();
115 if (isChecked && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
116 (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
117 mWifiManager.setWifiApEnabled(null, false);
118 }
119
120 if (mWifiManager.setWifiEnabled(isChecked)) {
121 // Intent has been taken into account, disable until new state is active
122 mSwitch.setEnabled(false);
123 } else {
124 // Error
125 Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show();
126 }
127 }
可以看出当不是AP时,是使能Wifi,默认行为是根据配置挑选出AP进行连接。
另外的两个类的解释:
WifiConfigController是MVC中的Controller
WifiDialog是单击的config对话框
连接和DHCP过程中显示的字符串xml文件如下:
In /packages/apps/Settings/res/values/arrays.xml
223 <!-- Wi-Fi settings -->
224
225 <!-- Match this with the order of NetworkInfo.DetailedState. --> <skip />
226 <!-- Wi-Fi settings. The status messages when the network is unknown. -->
227 <string-array name="wifi_status">
228 <!-- Status message of Wi-Fi when it is idle. -->
229 <item></item>
230 <!-- Status message of Wi-Fi when it is scanning. -->
231 <item>Scanning\u2026</item>
232 <!-- Status message of Wi-Fi when it is connecting. -->
233 <item>Connecting\u2026</item>
234 <!-- Status message of Wi-Fi when it is authenticating. -->
235 <item>Authenticating\u2026</item>
236 <!-- Status message of Wi-Fi when it is obtaining IP address. -->
237 <item>Obtaining IP address\u2026</item>
238 <!-- Status message of Wi-Fi when it is connected. -->
239 <item>Connected</item>
240 <!-- Status message of Wi-Fi when it is suspended. -->
241 <item>Suspended</item>
242 <!-- Status message of Wi-Fi when it is disconnecting. -->
243 <item>Disconnecting\u2026</item>
244 <!-- Status message of Wi-Fi when it is disconnected. -->
245 <item>Disconnected</item>
246 <!-- Status message of Wi-Fi when it is a failure. -->
247 <item>Unsuccessful</item>
248 </string-array>
至此UI界面响应部分介绍完毕,下面看WifiManager.
WifiManager是WifiService的客户端接口的封装类,WifiService是服务实现,接口是IWifiManager。
In IWifiManager.aidl
32interface IWifiManager {};
In WifiService.java
public class WifiService extends IWifiManager.Stub {};
In WifiManager.java, NOT the intermmediate file of IWifiManager.aidl
485 public WifiManager(IWifiManager service, Handler handler) {
486 mService = service;
487 mHandler = handler;
488 }
In ContextImpl.java每个Activity关联的Contect会得到WIFI_SERVICE,然后构造WifiManager封装类。代码如下:
449 registerService(WIFI_SERVICE, new ServiceFetcher() {
450 public Object createService(ContextImpl ctx) {
451 IBinder b = ServiceManager.getService(WIFI_SERVICE);
452 IWifiManager service = IWifiManager.Stub.asInterface(b);
453 returnnew WifiManager(service, ctx.mMainThread.getHandler());
454 }});
几个同步操作的控制流示例:
WifiSettings=>WifiManager::reconnect(…)()||||
=>WifiService:: reconnect ()=>WifiStateMachine :: reconnectCommand()
Scanner=>WifiManager::startScanActive()|||| =>WifiService::startScan(active)=>WifiStateMachine ::startScan(active)
WifiEnabler=> WifiManager::setWifiEnabled() |||| =>WifiService::setWifiEnabled()=>WifiStateMachine ::setWifiEnabled()
WifiManager中大部分同步命令都是这么个流程;耗时的异步命令则是通过AsyncChannel发送给WifiService.
In WifiService.java
public class WifiService extends IWifiManager.Stub {};
WifiService线程的启动如下
In SystemServer.java
384 try {
385 Slog.i(TAG, "Wi-Fi P2pService");
386 wifiP2p = new WifiP2pService(context);
387 ServiceManager.addService(Context.WIFI_P2P_SERVICE, wifiP2p);
388 } catch (Throwable e) {
389 reportWtf("starting Wi-Fi P2pService", e);
390 }
391
392 try {
393 Slog.i(TAG, "Wi-Fi Service");
394 wifi = new WifiService(context);
395 ServiceManager.addService(Context.WIFI_SERVICE, wifi);
396 } catch (Throwable e) {
397 reportWtf("starting Wi-Fi Service", e);
398 }
WifiService的构造函数本质是启动了一个带有消息队列的线程做WifiService,两个Handler attach到该消息队列上。
428 HandlerThread wifiThread = new HandlerThread("WifiService");
429 wifiThread.start();
430 mAsyncServiceHandler = new AsyncServiceHandler(wifiThread.getLooper());
431 mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());
AsyncServiceHandler做为destHandler用于处理下行的来自WifiManager客户端的命令消息,而WifiStateMachineHandler做为srcHandler用于向WifiStateMachine.mSmhandler发送异步命令。现在的WifiStatemachine实现是和WifiService使用同一个looper,在同一个线程中,所以mAsyncServiceHandler、mWifiStateMachineHandler、WifiStateMachine.mSmhandler是使用同一个looper,运行在wifiThread中。WifiStateMachine籍由StateMachine具备有单独looper在单独线程运行的能力。
WifiSerivce作为一个service,WifiService.java在frameworks/base/services/java/com/android/server/目录下。但其实现使用的WifiStateMachine在frameworks/base/wifi/java/android/net/wifi/目录下。
WifiStateMachine状态机使用WifiNative wpa_ctrl和wpa_supplicant通讯, WifiMonitor监听wpa_supplicant的异步消息。因为WifiStateMachine的核心是状态机,所以其接口都是转换成命令投入状态机消息队列。WifiService传来的命令和WifiMonitor监听到的响应汇入WifiStateMachine的消息队列,驱动WifiStateMachine转动起来。
WifiStateMachine继承自StateMachine,是一个hierarchical state machine which processes messages and can have states arranged hierarchically,其细节参见StateMachine.java. WifiStateMachine的hierarchy如下图所示:
下面考察初始状态到Wifi使能扫描AP连接AP查询AP过程的状态机运转。
In WifiStateMachine.java
状态机会启动,进入InitialState状态。然后UI打开WifiEnabler最终WifiService:: setWifiEnabled会被执行到。相关代码如下:
553 public WifiStateMachine(Context context, String wlanInterface) {
649 if (DBG) setDbg(true);
650
651 //start the state machine
652 start();
653 }
680 public void setWifiEnabled(boolean enable) {
681 mLastEnableUid.set(Binder.getCallingUid());
682 if (enable) {
683 /* Argument is the state that is entered prior to load */
684 sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
685 sendMessage(CMD_START_SUPPLICANT);
686 } else {
687 sendMessage(CMD_STOP_SUPPLICANT);
688 /* Argument is the state that is entered upon success */
689 sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0));
690 }
691 }
在InitialState.enter()中,初始情况下WifiDriver是没有加载的,所以进入DriverUnloadedState状态;
然后setWifiEnabled向状态机发出CMD_LOAD_DRIVER with WIFI_STATE_ENABLING和CMD_START_SUPPLICANT两个命令。
在DriverUnloadedState状态下,处理CMD_LOAD_DRIVER命令,向WifiP2pService发送WIFI_ENABLE_PENDING命令,也就是说通知WifiP2pService需要退出要启用Wifi了,因为WifiService和WifiP2pService同一时刻只能使用一个,然后转到WaitForP2pDisableState状态等待WifiP2pService的响应。
在WaitForP2pDisableState状态下收到WifiP2pService的响应WIFI_ENABLE_PROCEED,转到DriverLoadingState状态加载Wifi driver;对于其它请求会deferMessage到以后状态处理。
在DriverLoadingState状态启用线程异步加载Wifi driver,当加载成功时,向状态机自身发送消息CMD_LOAD_DRIVER_SUCCESS驱动状态机转换到DriverLoadedState,加载失败则发送消息CMD_LOAD_DRIVER_FAILED驱动状态机转换到DriverFailedState状态(会经由DriverUnloadedState)。
在DriverLoadedState状态下处理setWifiEnabled发出的defer到此的第二个命令CMD_START_SUPPLICANT,加载网卡芯片固件,启动wpa_supplicant,启动WifiMonitor接收wpa_supplicant的消息,转换状态到SupplicantStartingState等待WifiMonitor的事件(wpa_supplicant接入成功事件)。
在SupplicantStartingState状态下收到WifiMonitor. SUP_CONNECTION_EVENT表接入wpa_supplicant成功,regulate国家代号,初始化一些表征wpa_s的字段,转换状态至DriverStartedState;对于其它命令,defer处理。
经SupplicantStartedState.enter()和DriverStartedState.enter()中使用CMD_SET_COUNTRY_CODE和CMD_SET_FREQUENCY_BAND设置国家代号和频段,在本状态处理这两个命令;这两个命令是要发送给芯片的,所以在固件和驱动加载好后发送。设置好频段后,就向自身发送消息CMD_START_SCAN启动一次active scan。然后经由状态ConnectModeState转换状态至DisconnectedState。
在DisconnectedState下收到扫描结果WifiMonitor.SCAN_RESULTS_EVENT,消息路由给父状态SupplicantStartedState的processMessage进行处理,获取扫描结果并保存。在此状态下SupplicantStartedState处理与AP配置相关的命令,ConnectModeState处理与AP连接相关的命令。例如可以配置某个AP,然后请求connectNetwork,对于CMD_CONNECT_NETWORK,会使用Connect相关状态ConnectModeState.processMessage()处理,启动AP关联,然后转换状态到DisconnectingState与旧AP断开连接与新AP建立连接。
在DisconnectingState状态收到关联到新AP成功消息WifiMonitor.NETWORK_CONNECTION_EVENT后,使用ConnectModeState.processMessage处理,记录关联上的SSID,BSSID,获取AP信号强度和速度,广播消息,转换状态至ConnectingState进行IP获取。
在ConnectingState.enter()中使用静态IP或DHCP获取IP,配置IP,成功则转入ConnectedState状态。
在ConnectedState状态下,可以处理配置AP、连接新的AP、断开连接等,还有扫描请求,另外还要查询当前连接AP的信号强度和网络流量信息,在状态栏显示Wifi状态图标。这时通过CMD_RSSI_POLL命令实现的,当WifiService构造时,允许enableRssiPolling,一次CMD_ENABLE_RSSI_POLL会引发定时的CMD_RSSI_POLL,当屏幕未关闭时,CMD_RSSI_POLL就会定时向自身状态机发送;然后ConnectedState.processMessage调用fetchRssiAndLinkSpeedNative()处理CMD_RSSI_POLL。
以上是扫描、关联的状态转换过程,状态转化图大致如下:
其余断开连接、扫描模式等状态转换根据情景,参见WifiStateMachine.java代码。
WifiStateMachine状态机和wpa_supplicant通信,因为wpa_supplicant的接口是两个阻塞模式的unix domain socket,一个用于控制一个用于向请求者返回异步响应,所以决定了WifiStateMachine和wpa_supplicant之间的软件结构是有一个接收线程阻塞在读端口上,而写端口时根据速率匹配看是否需要单独的发送进程。
WifiStateMachine和wpa_supplicant之间的数据通信量是很小的,所以不需要单独的发送进程,只需要一个常接收阻塞的接收线程即可,这个线程就是WifiMonitor。WifiMonitor就是个ReceiverThread加消息协议转换。WifiMonitor在使用unix domain socket ctrl interface时是poll在socket上,有wpa_supplicant发回的数据时,阻塞的recv返回。
两个结构图如下:
WifiStateMachine的方法中同步的带有’sync’字样,异步的是直接发送消息到状态机消息队列,通过WifiMonitor异步收集结果。同步和异步都是基于AsyncChannel,同步只不过是在异步的AsyncChannel基础上加了同步(阻塞)保证机制。
WifiMonitor
294 class MonitorThread extends Thread {
295 public MonitorThread() {
296 super("WifiMonitor");
297 }
298
299 public void run() {
300
301 if (connectToSupplicant()) {
302 // Send a message indicating that it is now possible to send commands
303 // to the supplicant
304 mStateMachine.sendMessage(SUP_CONNECTION_EVENT);
305 } else {
306 mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
307 return;
308 }
309
310 //noinspection InfiniteLoopStatement
311 for (;;) {
312 String eventStr = WifiNative.waitForEvent();
313
……..
}
}
android_net_wifi_Wifi.cpp
120static jboolean android_net_wifi_loadDriver(JNIEnv* env, jobject)
121{
122 return (jboolean)(::wifi_load_driver() == 0);
123}
290static jboolean android_net_wifi_reconnectCommand(JNIEnv* env, jobject)
291{
292 return doBooleanCommand("OK", "RECONNECT");
293}
WifiNative.java中使用reconnectCommand方法时没有检查返回值,所以消息估计是异步传回。
static jboolean doBooleanCommand(const char* expect, const char* fmt, ...)
static int doCommand(const char *cmd, char *replybuf, int replybuflen)
call
875int wifi_command(const char *command, char *reply, size_t *reply_len) @ wifi_bcm.c
876{
877 return wifi_send_command(ctrl_conn, command, reply, reply_len);
878}
Wifi driver加载
bcm4330的wifi驱动可能是bcmdhd.so或dhd.so
#WIFI_DRIVER_MODULE_PATH := "/system/etc/bcm4330/dhd.ko"
insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG)
也可能是builtIn的
LOGI("Using BuildIn WiFi driver");
property_set(DRIVER_PROP_NAME, "ok");
wpa_ctrl连接wpa_supplicant
wpa_supplicant启动后会根据wpa_supplicant.conf配置自己。ctrl_interface=value这项就是ctrl_interface对应unix domain socket(DGRAM)的服务端口文件名,和wpa_cli等约定连接端口。value可以是ifacename或者DIR=一个目录。
wifi_bcm.c中的update_ctrl_interface,就是从config file里面找ctrl_interface=wlan0这项,生成unix domain socket的监听端口文件名。当ctrl_interface=wlan0时,wpa_supplicant系统会生成/dev/socket/wpa_wlan0。当是一个目录的时候,可能在其下生成什么文件名。
目标文件名是/dev/socket/wpa_wlan0,是socket(DGRAM)的服务端,这个是由wpa_supplicant打开的。参见init.Manufacture.rc。wpa_cli使用wpa_ctrl_open去socket connect连接这个服务名。Android修改了wpa_supplicant打开socket的代码,是在init.Manufacture.rc中在init阶段打开,wpa_supplicant在wpa_supplicant_ctrl_iface_init()时查找该socket直接使用。注意由于以下没有-g选项,所以wpa_supplicant_global_ctrl_iface_init()时的params.ctrl_interface is NULL。选项中传入的interface会wpa_supplicant_add_iface(global, &ifaces[i]) @main.c
service wpa_supplicant /system/bin/logwrapper /system/bin/wpa_supplicant -iwlan0 -puse_p2p_group_interface=1 -Dnl80211
class late_start
user root
group wifi inet
socket wpa_wlan0 dgram 660 wifi wifi
disabled
oneshot
使用单独的wpa_cli(link with wpa_ctrl.c)时,使用如下命令去连接wpa_supplicant
wpa_cli -p /dev/socket/ -i wpa_wlan0
连接成功后,wpa_cli程序会在某个目录下新建两个unix domain socket的两个local文件名。
srw-rw---- system wifi 2013-04-10 17:33 wpa_ctrl_337-1
srw-rw---- system wifi 2013-04-10 17:33 wpa_ctrl_337-2
该目录由以下两个宏指定
hardware/libhardware_legacy/wifi/Android.mk
3LOCAL_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
4LOCAL_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_PREFIX=\"wpa_ctrl_\"
其中/data/misc/wifi/sockets是使用wpa_cli库接口建立unix domain socket时的本地文件目录。这两个宏构成如/data/misc/wifi/sockets/ wpa_ctrl_pid-nn的本地文件名,就是unix domain socket的本地文件名。这儿和本地文件名bind的好处是把pid加到文件名中,server端因为是DGRAM socket,不能accept,recvfrom就可以得到该client socket的地址可以知道socket的pid,另外本地可以使用文件名chown/chmod。
wpa_cli使用wpa_ctrl_open创建两个socket,bind本地文件名,connect wpa_supplicant接口名(对于DGRAM,connect并没有真正的作用,仅是做peer addr绑定);wpa_supplicant不需要accept直接在其socket上recvfrom;当wpa _cli wpa_ctrl_attach其中一个时,从该socket读出”ATTACH”/”DETACH”,然后可以这个socket以后是monitor socket,因此以后不必在从此socket接收数据。
wpa_cli为了在wpa_ctrl_request出错时,退出recv(monitor_socket),用了socketpair中的读端socket和monitor_socket一起select,从而出错时正常退出。
具体参见wpa_ctrl.c文件。
当wpa_supplicant和driver和firmware不是手动加载的时候,直接打开WifiEnabler,则已打开的wpa_ctrl的事件也会输出到wpa_cli。wpa_supplicant会把消息输出到attach到其上的socket上,代码在wpa_supplicant_ctrl_iface_send()中把msg送到每个monitor socket。
247static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
248 const char *txt, size_t len)
249{
250 struct wpa_supplicant *wpa_s = ctx;
251 if (wpa_s == NULL || wpa_s->ctrl_iface == NULL)
252 return;
253 wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len);
254}
可以修改wpa_cli的源码,不到monitor_socket attach或者做了attach收到消息不打印,从而避免干扰。但是需要记住,同时有两个wpa_ctrl程序在操作wpa_supplicant。
wpa_supplicant的启动
int wifi_start_supplicant_common(const char *config_file)
{
property_get("wifi.interface", iface, WIFI_TEST_INTERFACE);
property_get("persist.wpa_supplicant.debug", supp_debug, "false");
if (strcmp(supp_debug, "true") == 0) {
SLOGI("persist.wpa_supplicant.debug property true");
snprintf(daemon_cmd, PROPERTY_VALUE_MAX, "%s:-i%s -c%s -ddd", SUPPLICANT_NAME, iface, config_file);
} else {
SLOGI("persist.wpa_supplicant.debug property false");
snprintf(daemon_cmd, PROPERTY_VALUE_MAX, "%s:-i%s -c%s", SUPPLICANT_NAME, iface, config_file);
}
property_set("ctl.start",daemon_cmd);
}
启动中会有log输出;前一条log表明wpa_supplicant的log输出到logger。
9738 log 0:00 /system/bin/logwrapper /system/bin/wpa_supplicant -iwlan0 -puse_p2p_group_interface=1 -Dnl80211 -iwlan0 -c/data/misc/wifi/wpa_supplicant.conf -ddd
9740 wifi 0:23 /system/bin/wpa_supplicant -iwlan0 -puse_p2p_group_interface=1 -Dnl80211 -iwlan0 -c/data/misc/wifi/wpa_supplicant.conf -ddd
注意 –g选项表示全局ctrl_interface,是从选项传入,对应wpa_supplicant源码中的global_ctrl_interface;以后加入的interface,对应源码中的ctrl_interface;如果global有选项指定覆盖新加入interface的选项,那么后加入的interface对应的参数会被global覆盖选项指定的参数覆盖掉。其余选项指定参数会覆盖config文件中的参数。优先级是覆盖选项参数>选项参数>config文件中参数。可覆盖的主要是driver和ctrl_interface。
总体结构。UML struct wpa_supplicant struct wpa_interface。具体组织结构和数据结构参见developers guidehttp://w1.fi/wpa_supplicant/wpa_supplicant-devel.pdf
wpa_supplicant使用eloop接收unix domain socket传来的命令,使用libnl和nl80211交互。
接收wpa_supplicant客户端control命令的unix domain socket函数主要入口是wpa_supplicant_ctrl_iface_receive,接收到命令数据后,除了ATTACH/DETACH直接处理,会调用wpa_supplicant_ctrl_iface_process处理;ATTACH/DETACH其实是monitor_event端口发过来的,control端口发来的命令都是交由wpa_supplicant_ctrl_iface_process处理。
wpa_supplicant_ctrl_iface_process
{
3286 } else if (os_strcmp(buf, "RECONNECT") == 0) {
3287 if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
3288 reply_len = -1;
3289 else if (wpa_s->disconnected) {
3290 wpa_s->disconnected = 0;
3291 wpa_s->reassociate = 1;
3292 wpa_supplicant_req_scan(wpa_s, 0, 0);
3293 }
3521 } else if (os_strcmp(buf, "SCAN") == 0) {
3522 if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
3523 reply_len = -1;
3524 else {
3525 if (!wpa_s->scanning &&
3526 ((wpa_s->wpa_state <= WPA_SCANNING) ||
3527 (wpa_s->wpa_state == WPA_COMPLETED))) {
3528 wpa_s->scan_req = 2;
3529 wpa_supplicant_req_scan(wpa_s, 0, 0);
3530 } else {
3531 wpa_printf(MSG_DEBUG, "Ongoing scan action - "
3532 "reject new request");
3533 reply_len = os_snprintf(reply, reply_size,
3534 "FAIL-BUSY\n");
3535 }
3536 }
3537 } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) {
3538 reply_len = wpa_supplicant_ctrl_iface_scan_results(
3539 wpa_s, reply, reply_size);
3540 } else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) {
3541 if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15))
3542 reply_len = -1;
3543 } else if (os_strncmp(buf, "ENABLE_NETWORK ", 15) == 0) {
3544 if (wpa_supplicant_ctrl_iface_enable_network(wpa_s, buf + 15))
3545 reply_len = -1;
3546 } else if (os_strncmp(buf, "DISABLE_NETWORK ", 16) == 0) {
3547 if (wpa_supplicant_ctrl_iface_disable_network(wpa_s, buf + 16))
3548 reply_len = -1;
3549 } else if (os_strcmp(buf, "ADD_NETWORK") == 0) {
3550 reply_len = wpa_supplicant_ctrl_iface_add_network(
3551 wpa_s, reply, reply_size);
3552 } else if (os_strncmp(buf, "REMOVE_NETWORK ", 15) == 0) {
3553 if (wpa_supplicant_ctrl_iface_remove_network(wpa_s, buf + 15))
3554 reply_len = -1;
3555 } else if (os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
3556 if (wpa_supplicant_ctrl_iface_set_network(wpa_s, buf + 12))
3557 reply_len = -1;
3558 } else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) {
3559 reply_len = wpa_supplicant_ctrl_iface_get_network(
3560 wpa_s, buf + 12, reply, reply_size);
3561#ifndef CONFIG_NO_CONFIG_WRITE
3562 } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
3563 if (wpa_supplicant_ctrl_iface_save_config(wpa_s))
3564 reply_len = -1;
3565#endif /* CONFIG_NO_CONFIG_WRITE */
3566 } else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) {
3567 reply_len = wpa_supplicant_ctrl_iface_get_capability(
3568 wpa_s, buf + 15, reply, reply_size);
3569 } else if (os_strncmp(buf, "AP_SCAN ", 8) == 0) {
3570 if (wpa_supplicant_ctrl_iface_ap_scan(wpa_s, buf + 8))
3571 reply_len = -1;
3572 } else if (os_strncmp(buf, "SCAN_INTERVAL ", 14) == 0) {
}
wpa_supplicant_ctrl_iface_process对于命令要么在wpa_supplicant内通过wpa_supplicant_dddd_nnnn自己处理,要么交给driver wpa_drv_dddd_nnnn去处理。命令处理或发送给driver后,将结果直接通过sendto源地址。
异步CTRL-EVENT-消息是通过wpa_msg(MSG_INFO)和wpa_msg_ctrl(MSG_INFO)发送给monitor socket,如果修改成都使用同一接口则风格更统一,更具可读性。
wpa_supplicant_driver通过libnl和nl80211网卡驱动通信,详述driver_nl80211和kernel通信的接口。主要使用了一个普通socket、两个NETLINK_GENERIC socket、一个NETLINK_ROUTE socket、一个/dev/rkfill文件,外加一个monitor PACKET RAW socket。普通socket使用网卡相关IOCTL和指定的网卡通信,具体见normal socket ioctl to net interface ioctl 文;两个NETLINK_GENERTIC socket一个用于nl80211 control一个用于nl80211 event接收,接收的event有nl80211 scan、nl80211 mlme、nl80211 regulatory还有registerred frame;一个NETLINK_ROUTE主要用于nl80211 route event接收处理link layer事件;/dev/rfkill用于rfkill;而monitor socket用于实现wpa_supplicant AP hotspot。
这几个socket创建如下:
ioctl_socket = socket(PF_INET, SOCK_DGRAM, 0);
genl_ctrl_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_GENERTIC);
genl_event_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_GENERTIC);
nl_route_event_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
monitor_socket = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
Event、monitor和/dev/rfkill的recv_cb函数如下:
genl_event_socket
=> wpa_driver_nl80211_event_receive()
=> process_event() for (NL_CB_VALID - NL_CB_CUSTOM)
nl_route_event_socket
=> netlink_receive()
=> wpa_driver_nl80211_event_rtm_newlink() for RTM_NEWLINK
=> wpa_driver_nl80211_event_rtm_dellink() for RTM_DELLINK
=> wpa_driver_nl80211_event_link()
=> wpa_supplicant_event()
/dev/rfkill
=> rfkill_receive()
=> wpa_driver_nl80211_rfkill_blocked()
=> wpa_driver_nl80211_rfkill_unblocked()
monitor_socket
=> handle_monitor_read()
875int wifi_command(const char *command, char *reply, size_t *reply_len) @ wifi_bcm.c
发送命令到wpa_supplicant的监听socket上,该socket的recv函数是wpa_supplicant_ctrl_iface_receive,在wpa_supplicant的初始化过程中注册
main()
=> wpa_supplicant_add_iface
=> wpa_supplicant_init_iface
=> wpa_supplicant_ctrl_iface_init
=> eloop_register_read_sock.
socket receive cb注册是使用eloop做的,以nl80211 event socket为例,是在wpa_driver_nl80211_init_nl中,wpa_driver_nl80211_event_receive注册到netlink socket的读处理上。
eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event),
wpa_driver_nl80211_event_receive, drv,
drv->nl_handle_event);
下面大致介绍扫描和连接的过程。首先是扫描的过程,
在客户端命令扫描wpa_supplicant_ctrl_interface_process处理”SCAN”命令时,会wpa_supplicant_req_scan();其余连接过程也可能会请求扫描一下看该AP是否还存在,如
wpa_supplicant_ctrl_iface_select_network - Select the network and disable others
@ssid: wpa_ssid structure for a configured network or %NULL for any network
wpa_supplicant_ctrl_iface_select_network => wpa_supplicant_select_network =>wpa_supplicant_req_scan(wpa_s, 0, 0);
wpa_supplicant_enable_network - Mark a configured network as enabled
@ssid: wpa_ssid structure for a configured network or %NULL
wpa_supplicant_ctrl_iface_enable_network => wpa_supplicant_enable_network =>wpa_supplicant_req_scan(wpa_s, 0, 0);
wpa_supplicant_req_scan具体调用流程如下:
void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)进行延迟scan请求。
static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx);进行扫描触发。
int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params)真正做触发scan。
int wpa_drv_scan(struct wpa_supplicant *wpa_s, struct wpa_ssid **ssid_ptr)调用wpa_driver user_space驱动如driver_nl80211进行扫描。
static int wpa_driver_nl80211_scan(void *priv, struct wpa_driver_scan_params *params)发送NL80211_CMD_TRIGGER_SCAN给kernel驱动。
wpa_supplicant_scan()会根据不同ap_scan做扫描或根据驱动关联结果发送关联event。大多数情况下ap_scan为1,trigger scan。
当内核扫描完成后会发送NL80211_CMD_NEW_SCAN_RESULTS通知,process_event() @ driver_nl80211.c调用send_scan_event()调用wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event) @ event.c处理。
void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data)
2155 case EVENT_SCAN_RESULTS:
2156 wpa_supplicant_event_scan_results(wpa_s, data);
2157 break;
接收扫描结果并且根据配置从扫描结果中选择AP进行连接。下面着重考察连接过程。
wpa_supplicant_event_scan_results
_wpa_supplicant_event_scan_results => wpa_supplicant_get_scan_results()
wpa_supplicant_pick_network
wpa_supplicant_select_bss
wpa_scan_res_match
现在的是match之后,下一个是什么过程。要找到:从connect命令后找,启动搜索,搜索结果,从configed APs中匹配AP。
连接前启动扫描,扫描结果匹配后,自然是连接。就从_wpa_supplicant_event_scan_results在pick network之后看。
if (selected) {
int skip;
skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid, scan_res);
wpa_scan_results_free(scan_res);
if (skip)
return 0;
wpa_supplicant_connect(wpa_s, selected, ssid);
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
}
wpa_supplicant_connect()会根据状态调用wpa_supplicant_associate(wpa_s,selected, ssid)进行AP关联。另外wpa_supplicant_ctrl_iface_roam()处理时也会调用wpa_supplicant_connect()。
wpa_supplicant_associate是连接和验证的入口。_wpa_supplicant_event_scan_results在wpa_supplicant_connect()连接不成功时,会wpa_supplicant_associate(wpa_s,NULL, ssid)去尝试关联其它的AP;在wpa_supplicant_scan()中根据不同ap_scan模式和配置,也会直接调用wpa_supplicant_assoc(wpa_s,NULL, ssid);或者在sme_event_auth()即通过auth后进行特定AP的关联wpa_supplicant_assoc(wpa_s,wpa_s->current_bss, wpa_s->current_ssid),这是Authenticate和Associate分步的情况。
wpa_supplicant_associate
=> wpa_drv_associate
=> wpa_driver_nl80211_associate when drvier_nl80211 is used.
=> wpa_driver_nl80211_ibss for IBSS mode.
=> wpa_driver_nl80211_connect for INFRA non-SME mode
=> or send NL80211_CMD_ASSOCIATE for SME mode after AUTHENTICATED
wpa_driver_nl80211_ibss首先将驱动和固件设置到IBSS工作模式,然后发送JOIN_IBSS命令。
4709static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
4710 struct wpa_driver_associate_params *params)
4711{
4712 struct nl_msg *msg;
4713 int ret = -1;
4714 int count = 0;
4715
4716 wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex);
4717
4718 if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode)) {
4719 wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
4720 "IBSS mode");
4721 return -1;
4722 }
4723
4724retry:
4725 msg = nlmsg_alloc();
4726 if (!msg)
4727 return -1;
4728
4729 genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
4730 NL80211_CMD_JOIN_IBSS, 0);
4731 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
4732
4733 if (params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
4734 goto nla_put_failure;
4735
4736 wpa_hexdump_ascii(MSG_DEBUG, " * SSID",
4737 params->ssid, params->ssid_len);
4738 NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
4739 params->ssid);
4740 os_memcpy(drv->ssid, params->ssid, params->ssid_len);
4741 drv->ssid_len = params->ssid_len;
4742
4743 wpa_printf(MSG_DEBUG, " * freq=%d", params->freq);
4744 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
4745
4746 ret = nl80211_set_conn_keys(params, msg);
4747 if (ret)
4748 goto nla_put_failure;
4749
4750 if (params->wpa_ie) {
4751 wpa_hexdump(MSG_DEBUG,
4752 " * Extra IEs for Beacon/Probe Response frames",
4753 params->wpa_ie, params->wpa_ie_len);
4754 NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
4755 params->wpa_ie);
4756 }
4757
4758 ret = send_and_recv_msgs(drv, msg, NULL, NULL);
4759 msg = NULL;
4760 if (ret) {
4761 wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)",
4762 ret, strerror(-ret));
4763 count++;
4764 if (ret == -EALREADY && count == 1) {
4765 wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after "
4766 "forced leave");
4767 nl80211_leave_ibss(drv);
4768 nlmsg_free(msg);
4769 goto retry;
4770 }
4771
4772 goto nla_put_failure;
4773 }
4774 ret = 0;
4775 wpa_printf(MSG_DEBUG, "nl80211: Join IBSS request sent successfully");
4776
4777nla_put_failure:
4778 nlmsg_free(msg);
4779 return ret;
4780}
INFRA模式时,wpa_driver_nl80211_connect被调用之前会设置网卡工作模式为BSS模式。connect过程向驱动发送NL80211_CMD_CONNECT。
4783static int wpa_driver_nl80211_connect(
4784 struct wpa_driver_nl80211_data *drv,
4785 struct wpa_driver_associate_params *params)
4786{
4787 struct nl_msg *msg;
4788 enum nl80211_auth_type type;
4789 int ret = 0;
4790 int algs;
4791
4792 msg = nlmsg_alloc();
4793 if (!msg)
4794 return -1;
4795
4796 wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
4797 genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
4798 NL80211_CMD_CONNECT, 0);
4799
4800 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
4801 if (params->bssid) {
4802 wpa_printf(MSG_DEBUG, " * bssid=" MACSTR,
4803 MAC2STR(params->bssid));
4804 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
4805 }
4806 if (params->freq) {
4807 wpa_printf(MSG_DEBUG, " * freq=%d", params->freq);
4808 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
4809 }
4810 if (params->ssid) {
4811 wpa_hexdump_ascii(MSG_DEBUG, " * SSID",
4812 params->ssid, params->ssid_len);
4813 NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
4814 params->ssid);
4815 if (params->ssid_len > sizeof(drv->ssid))
4816 goto nla_put_failure;
4817 os_memcpy(drv->ssid, params->ssid, params->ssid_len);
4818 drv->ssid_len = params->ssid_len;
4819 }
4820 wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, params->wpa_ie_len);
4821#ifdef WAPI
4822 wpa_printf(MSG_DEBUG, "%s: params->wpa_ie_len=%d\n", __FUNCTION__, params->wpa_ie_len);
4823 wpa_printf(MSG_DEBUG, "%s: params->wpa_ie: [%02x][%02x][%02x][%02x]\n",
4824 __FUNCTION__, params->wpa_ie[0], params->wpa_ie[1], params->wpa_ie[2], params->wpa_ie[3]);
4825
4826 if (params->wpa_ie[0] == WLAN_EID_WAPI) {
4827 drv->ap_wapi_ie = params->ap_wapi_ie ;
4828 drv->ap_wapi_ie_len = params->ap_wapi_ie_len ;
4829 }
4830#endif
4831
4832 if (params->wpa_ie)
4833 NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
4834 params->wpa_ie);
4835
4836 algs = 0;
4837 if (params->auth_alg & WPA_AUTH_ALG_OPEN)
4838 algs++;
4839 if (params->auth_alg & WPA_AUTH_ALG_SHARED)
4840 algs++;
4841 if (params->auth_alg & WPA_AUTH_ALG_LEAP)
4842 algs++;
4843 if (algs > 1) {
4844 wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic "
4845 "selection");
4846 goto skip_auth_type;
4847 }
4848
4849 if (params->auth_alg & WPA_AUTH_ALG_OPEN)
4850 type = NL80211_AUTHTYPE_OPEN_SYSTEM;
4851 else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
4852 type = NL80211_AUTHTYPE_SHARED_KEY;
4853 else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
4854 type = NL80211_AUTHTYPE_NETWORK_EAP;
4855 else if (params->auth_alg & WPA_AUTH_ALG_FT)
4856 type = NL80211_AUTHTYPE_FT;
4857#ifdef WAPI
4858 else if (params->wpa_ie[0] == WLAN_EID_WAPI) {
4859 type = NL80211_AUTHTYPE_OPEN_SYSTEM;
4860 }
4861#endif
4862
4863 else
4864 goto nla_put_failure;
4865
4866 wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
4867 NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
4868
4869skip_auth_type:
4870 if (params->wpa_ie && params->wpa_ie_len) {
4871 enum nl80211_wpa_versions ver;
4872
4873 if (params->wpa_ie[0] == WLAN_EID_RSN)
4874 ver = NL80211_WPA_VERSION_2;
4875#ifdef WAPI
4876 else if (params->wpa_ie[0] == WLAN_EID_WAPI) { /* wapi */
4877 wpa_printf(MSG_DEBUG, " * IW_AUTH_WAPI_VERSION_1");
4878 ver = NL80211_WAPI_VERSION_1;
4879 }
4880#endif
4881 else
4882 ver = NL80211_WPA_VERSION_1;
4883
4884 wpa_printf(MSG_DEBUG, " * WPA Version %d", ver);
4885 NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver);
4886 }
4887#ifdef WAPI
4888 else if(params->wpa_ie[0] == WLAN_EID_WAPI) {
4889 enum nl80211_wpa_versions ver;
4890
4891 ver = NL80211_WAPI_VERSION_1;
4892 NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver);
4893 wpa_printf(MSG_DEBUG, " * NO params->wpa_ie && params->wpa_ie_len");
4894 }
4895#endif
4896
4897 if (params->pairwise_suite != CIPHER_NONE) {
4898 int cipher;
4899
4900 switch (params->pairwise_suite) {
4901 case CIPHER_WEP40:
4902 cipher = WLAN_CIPHER_SUITE_WEP40;
4903 break;
4904 case CIPHER_WEP104:
4905 cipher = WLAN_CIPHER_SUITE_WEP104;
4906 break;
4907 case CIPHER_CCMP:
4908 cipher = WLAN_CIPHER_SUITE_CCMP;
4909 break;
4910 case CIPHER_TKIP:
4911 default:
4912 cipher = WLAN_CIPHER_SUITE_TKIP;
4913 break;
4914 }
4915#ifdef WAPI
4916 if (params->wpa_ie[0] == WLAN_EID_WAPI) { /* wapi */
4917 wpa_printf(MSG_DEBUG, " * Set SUITES_PAIRWISE to WLAN_CIPHER_SUITE_SMS4");
4918 cipher = WLAN_CIPHER_SUITE_SMS4;
4919 }
4920#endif
4921 NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
4922#ifdef WAPI
4923 } else {
4924 if (params->wpa_ie[0] == WLAN_EID_WAPI) { /* wapi */
4925 int cipher;
4926 wpa_printf(MSG_DEBUG, " * Set SUITES_PAIRWISE to WLAN_CIPHER_SUITE_SMS4");
4927 cipher = WLAN_CIPHER_SUITE_SMS4;
4928
4929 params->pairwise_suite = WLAN_CIPHER_SUITE_SMS4;
4930
4931 NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
4932
4933 }
4934#endif
4935
4936 }
4937
4938 if (params->group_suite != CIPHER_NONE) {
4939 int cipher;
4940
4941 switch (params->group_suite) {
4942 case CIPHER_WEP40:
4943 cipher = WLAN_CIPHER_SUITE_WEP40;
4944 break;
4945 case CIPHER_WEP104:
4946 cipher = WLAN_CIPHER_SUITE_WEP104;
4947 break;
4948 case CIPHER_CCMP:
4949 cipher = WLAN_CIPHER_SUITE_CCMP;
4950 break;
4951 case CIPHER_TKIP:
4952 default:
4953 cipher = WLAN_CIPHER_SUITE_TKIP;
4954 break;
4955 }
4956 NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
4957 }
4958
4959 if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
4960 params->key_mgmt_suite == KEY_MGMT_PSK
4961#ifdef WAPI
4962 || params->key_mgmt_suite == KEY_MGMT_WAPI_PSK
4963 || params->key_mgmt_suite == KEY_MGMT_WAPI_CERT
4964#endif
4965 ) {
4966 int mgmt = WLAN_AKM_SUITE_PSK;
4967 switch (params->key_mgmt_suite) {
4968 case KEY_MGMT_802_1X:
4969 mgmt = WLAN_AKM_SUITE_8021X;
4970 break;
4971 case KEY_MGMT_PSK:
4972#ifdef WAPI
4973 mgmt = WLAN_AKM_SUITE_PSK;
4974 break;
4975 case KEY_MGMT_WAPI_PSK:
4976 mgmt = WLAN_AKM_SUITE_WAPI_PSK;
4977 break;
4978 case KEY_MGMT_WAPI_CERT:
4979 mgmt = WLAN_AKM_SUITE_WAPI_CERT;
4980 break;
4981#endif
4982 default:
4983 mgmt = WLAN_AKM_SUITE_PSK;
4984 break;
4985 }
4986
4987 NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
4988 }
4989
4990 ret = nl80211_set_conn_keys(params, msg);
4991 if (ret)
4992 goto nla_put_failure;
4993
4994 ret = send_and_recv_msgs(drv, msg, NULL, NULL);
4995 msg = NULL;
4996 if (ret) {
4997 wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
4998 "(%s)", ret, strerror(-ret));
4999 goto nla_put_failure;
5000 }
5001 ret = 0;
5002 wpa_printf(MSG_DEBUG, "nl80211: Connect request send successfully");
5003
5004nla_put_failure:
5005 nlmsg_free(msg);
5006 return ret;
5007
5008}
设置网卡工作模式 INFRA还是ADHOC的函数是nl80211_set_mode()。
5148static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
5149 int ifindex, int mode)
5150{
5151 struct nl_msg *msg;
5152 int ret = -ENOBUFS;
5153
5154 msg = nlmsg_alloc();
5155 if (!msg)
5156 return -ENOMEM;
5157
5158 genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
5159 0,NL80211_CMD_SET_INTERFACE, 0);
5160 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
5161 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode);
5162
5163 ret = send_and_recv_msgs(drv, msg, NULL, NULL);
5164 if (!ret)
5165 return 0;
5166nla_put_failure:
5167 wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
5168 " %d (%s)", ifindex, mode, ret, strerror(-ret));
5169 return ret;
5170}
当成功连接ibss后,
wpa_driver_nl80211_event_receive => process_event(NL80211_CMD_JOIN_IBSS) => mlme_event_join_ibss() => wpa_supplicant_event(drv->ctx,EVENT_ASSOC, NULL);
或者当INFRA时,
直接wpa_driver_nl80211_connect发送,连接成功会收到NL80211_CMD_CONNECT响应,用wpa_driver_nl80211_event_receive => process_event(NL80211_CMD_CONNECT) => mlme_event_connect=>wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);处理。
或者当SME时, AUTH和ASSOCIATE分离,
wpa_driver_nl80211_event_receive => process_event(NL80211_CMD_ASSOCIATE) => mlme_event(NL80211_CMD_ASSOCIATE) => mlme_event_assoc () => wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event)处理。
或者当扫描时wpa_supplicant_scan() with ap_scan==0表示由driver负责AP关联;当得到kernel driver已经关联到AP后,也会wpa_supplicant_gen_assoc_event= > wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data)
case EVENT_ASSOC:
wpa_supplicant_event_assoc(wpa_s, data);
break;
EVENT_ASSOC用于表示已经关联上AP。log输出片段如下:
E/wpa_supplicant( 5668): nl80211: Event message available
E/wpa_supplicant( 5668): nl80211: cmd response received-43(NL80211_CMD_JOIN_IBSS) 46(NL80211_CMD_CONNECT) for INFRA.
E/wpa_supplicant( 5668): nl80211: enter mlme_event_join_ibss
E/wpa_supplicant( 5668): nl80211: IBSS 72:d9:d2:6d:26:d2 joined
E/wpa_supplicant( 5668): wlan0: Event 0 received on interface wlan0
E/wpa_supplicant( 5668): wlan0: ASSOC notification
D/wpa_supplicant( 5668): wpa_supplicant_event_assoc: associated to a wapi network.
D/wpa_supplicant( 5668): wlan0: State: ASSOCIATING -> ASSOCIATED
mac layer me处理层面,mlme_event调用mlme_event_mgmt处理management帧事件,其余如data、control帧使用mlme_event_xxxx系列函数处理,然后调用wpa_supplicant_event通知wpa_s。
netlink-route事件
wpa_driver_nl80211_event_rtm_newlink是在wpa_driver_nl80211_init中注册的
cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
在driver_nl80211中,event上行路线:netlink_receive处理link事件,调用wpa_driver_nl80211_event_rtm_newlink/dellink,调用wpa_driver_nl80211_event_link处理link层面事件,调用wpa_supplicant_event通知wpa_s。
Driver config部分主要由nl80211 thin layer、linux cfg80211、vendor cfg80211部分组成,用户空间的NL80211_CMD发送给nl80211通过linux cfg80211转发或者直接发送给vendor cfg80211;vendor cfg80211控制芯片,收到操作完成的通知后,通过linux cfg80211接口经过nl80211向Multicast netlink socket发送通知给用户空间。
linux nl80211接口主要文件kernel/net/wireless/nl80211.c,向Generic Netlink注册了”nl80211” family和几个multicast group。除了nl80211,kernel/net/wireless/目录下其余文件也是linux cfg80211的实现文件,主要是core.c, scan.c, mlme.c, ibss.c, chan.c, util.c等。
vendor cfg80211 driver接口主要文件是kernel/drivers/net/wireless/bcmdhd/wl_cfg80211.c
设备注册,设备栈,rdev netdev wlan_dev sdio_func.dev mmc_card.dev
static int __init dhd_module_init(void) @ dhd_linux.c
late_initcall(dhd_module_init);
sdio_register_driver(&bcmsdh_sdmmc_driver)
sdio host controller platform device注册时会检测连接的卡设备,读出设备id,通过与bcmsdh_sdmmc_driver.id_table匹配match,然后调用bcmsdh_sdmmc_driver.probe来探测设备。大致原理见基于linux-2.6.38.8内核的SDIO/wifi驱动分析。
dhdsdio_probe一通初始化和资源分配在dhd_attach(osh, bus, SDPCM_RESERVE)中分配注册网络设备和无线设备及附加到IW。具体如下:
无线设备:wl_cfg80211_attach() allocaltes wireless_dev wdev = wl_alloc_wdev(dev);
and link it with wiphy and net_device;
wl_alloc_wdev really allocates wireless_dev using kzalloc and allocates wireless_dev.wiphy using wiphy_new(&wl_cfg80211_ops, sizeof(struct wl_priv));
and register wiphy with cfg80211 module using wiphy_register(wdev->wiphy);
网卡设备:分配ethernet网卡alloc_etherdev(sizeof(dhd)),在dhd_net_attach(dhd_pub_t *dhdp, int ifidx)中注册网卡register_netdev(net),完成网络设备的注册,完成从总线设备到网络功能设备的注册流程。
网卡驱动帧接收具体过程从略,同普通网卡驱动。
dhd_rx_frame接收网卡收到的帧或网卡的event帧,802.11帧读出来是802.2 LLC on 802.3 or Ethernet帧的格式,event帧格式是Ethernet II frame格式,length=ETHER_TYPE_BRCM>1536,其值做为protocol type解释。802.11帧去掉Ethernet帧头将802.2 LLC帧交netif_rx继续处理解出其中的ip packet交TCPIP stack处理。Event帧则剥离ether_hdr后直接由dhd_wl_host_event()处理。
dhd_wl_host_event
=> wl_host_event处理host感兴趣的事件
=> wl_iw_event处理wext范围内事件
=> wl_cfg80211_event把cfg80211相关事件放入处理队列!
wl_event_handler线程从队列取事件并调用wl->evt_handler[e->etype] (wl, netdev, &e->emsg, e->edata)处理注册了handler的事件。事件handler是在wl_init_event_handler中注册的。
5255static void wl_init_event_handler(struct wl_priv *wl)
5256{
5257 memset(wl->evt_handler, 0, sizeof(wl->evt_handler));
5258
5259 wl->evt_handler[WLC_E_SCAN_COMPLETE] = wl_notify_scan_status;
5260 wl->evt_handler[WLC_E_LINK] = wl_notify_connect_status;
5261 wl->evt_handler[WLC_E_DEAUTH_IND] = wl_notify_connect_status;
5262 wl->evt_handler[WLC_E_DEAUTH] = wl_notify_connect_status;
5263 wl->evt_handler[WLC_E_DISASSOC_IND] = wl_notify_connect_status;
5264 wl->evt_handler[WLC_E_ASSOC_IND] = wl_notify_connect_status;
5265 wl->evt_handler[WLC_E_REASSOC_IND] = wl_notify_connect_status;
5266 wl->evt_handler[WLC_E_ROAM] = wl_notify_roaming_status;
5267 wl->evt_handler[WLC_E_MIC_ERROR] = wl_notify_mic_status;
5268 wl->evt_handler[WLC_E_SET_SSID] = wl_notify_connect_status;
5269 wl->evt_handler[WLC_E_ACTION_FRAME_RX] = wl_notify_rx_mgmt_frame;
5270 wl->evt_handler[WLC_E_PROBREQ_MSG] = wl_notify_rx_mgmt_frame;
5271 wl->evt_handler[WLC_E_P2P_PROBREQ_MSG] = wl_notify_rx_mgmt_frame;
5272 wl->evt_handler[WLC_E_P2P_DISC_LISTEN_COMPLETE] = wl_cfgp2p_listen_complete;
5273 wl->evt_handler[WLC_E_ACTION_FRAME_COMPLETE] = wl_cfgp2p_action_tx_complete;
5274 wl->evt_handler[WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE] = wl_cfgp2p_action_tx_complete;
5275
5276}
下面描述scan和join/connect在cfg80211中的处理过程
static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
3533 request->dev = dev;
3534 request->wiphy = &rdev->wiphy;
3535 request->no_cck =
3536 nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
3537
3538 rdev->scan_req = request;
3539 err = rdev->ops->scan(&rdev->wiphy, dev, request);
ops scan field is wl_cfg80211_scan(), it calls
=> __wl_cfg80211_scan()
=> wl_do_escan() when e-scanner is used
=> wl_run_escan()向sdio接口发送命令
当芯片扫描完成后,发出WLC_E_SCAN_COMPLETE事件;驱动WLC_GET_SCAN_RESULTS,芯片返回WLC_E_SCAN_RESULTS事件?wl_escan_handler is called,这个是在wl_init_scan()中使用e-scanner时注册的,wl->evt_handler[WLC_E_ESCAN_RESULT] = wl_escan_handler;
scan results内核数据更新
wl_escan_handler中对扫描到的每个报告帧解析,添加到内核网卡数据结构bss_list中;
wl_escan_handler => wl_inform_bss => wl_inform_single_bss
连接成功AP后,也会更新该数据结构。
wl_bss_connect_done => wl_update_bss_info(not ibss) => wl_inform_single_bss
以后wl_inform_single_bss => cfg80211_inform_bss_frame => cfg80211_bss_update操作rb tree.
还有一条线 cfg80211_inform_bss => cfg80211_bss_update此条线能找到
wl_ibss_join_done => wl_update_ibss_info(ibss) => wl_inform_single_bss
NL80211_CMD_SET_INTERFACE command to kernel space driver =>
static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
1586 if (change)
1587 err =cfg80211_change_iface(rdev, dev, ntype, flags, ¶ms);
int cfg80211_change_iface(…)
846 err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev,
847 ntype, flags, params);
change_virtual_intf is wl_cfg80211_change_virtual_iface() for bcmdhd driver.在
wl_cfg80211在wl_cfg80211_change_virtual_iface()中设置到IBSS模式时没有执行,需要patch上WLC_SET_INFRA.
err = wldev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(infra), true);
另外地,wldev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(infra), true)在wl_config_ifmode中也被调用,而wl_config_ifmode在wl_cfg80211_up(void *para)=>__wl_cfg80211_up中被调用。
wl_cfg80211_up只在dhd驱动的open函数dhd_open(struct net_device *net)中使用一次,用于网卡打开进行配置使用,所以iwconfig可以使能网卡IBSS模式,但是wpa_supplicant不能使能网卡IBSS模式的原因大概在此。
static s32 wl_config_ifmode(struct wl_priv *wl, struct net_device *ndev, s32 iftype)
{
s32 infra = 0;
s32 err = 0;
s32 mode = 0;
switch (iftype) {
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_WDS:
WL_ERR(("type (%d) : currently we do not support this mode\n",
iftype));
err = -EINVAL;
return err;
case NL80211_IFTYPE_ADHOC:
mode = WL_MODE_IBSS;
break;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
mode = WL_MODE_BSS;
infra = 1;
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
mode = WL_MODE_AP;
infra = 1;
break;
default:
err = -EINVAL;
WL_ERR(("invalid type (%d)\n", iftype));
return err;
}
infra = htod32(infra);
err = wldev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(infra), true);
if (unlikely(err)) {
WL_ERR(("WLC_SET_INFRA error (%d)\n", err));
return err;
}
wl_set_mode_by_netdev(wl, ndev, mode);
return 0;
}
再看第二个步骤发送JOIN_IBSS for IBSS mode, or CONNECT for INFRA mode。
join_ibss in driver_nl80211.c
wpa_driver_nl80211_ibss
genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
NL80211_CMD_JOIN_IBSS, 0);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
kernel/net/wireless/nl80211.c is one driver top file for JOIN_IBSS。
4330static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
4331{
4332 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4333 struct net_device *dev = info->user_ptr[1];
4334 struct cfg80211_ibss_params ibss;
4335 struct wiphy *wiphy;
4336 struct cfg80211_cached_keys *connkeys = NULL;
4337 int err;
4338
4339 memset(&ibss, 0, sizeof(ibss));
4340
4341 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4342 return -EINVAL;
4343
4344 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
4345 !info->attrs[NL80211_ATTR_SSID] ||
4346 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4347 return -EINVAL;
4348
4349 ibss.beacon_interval = 100;
4350
4351 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
4352 ibss.beacon_interval =
4353 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
4354 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
4355 return -EINVAL;
4356 }
4357
4358 if (!rdev->ops->join_ibss)
4359 return -EOPNOTSUPP;
4360
4361 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
4362 return -EOPNOTSUPP; //这个东西并不影响IBSS逻辑,奇怪?
4363
4364 wiphy = &rdev->wiphy;
4365
4366 if (info->attrs[NL80211_ATTR_MAC])
4367 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4368 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4369 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4370
4371 if (info->attrs[NL80211_ATTR_IE]) {
4372 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4373 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4374 }
4375
4376 ibss.channel = ieee80211_get_channel(wiphy,
4377 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
4378 if (!ibss.channel ||
4379 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
4380 ibss.channel->flags & IEEE80211_CHAN_DISABLED)
4381 return -EINVAL;
4382
4383 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
4384 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
4385
4386 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
4387 u8 *rates =
4388 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4389 int n_rates =
4390 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4391 struct ieee80211_supported_band *sband =
4392 wiphy->bands[ibss.channel->band];
4393 int err;
4394 err = ieee80211_get_ratemask(sband, rates, n_rates,
4395 &ibss.basic_rates);
4396 if (err)
4397 return err;
4398 }
4399
4400 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
4401 !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
4402 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
4403 return -EINVAL;
4404
4405 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4406 connkeys = nl80211_parse_connkeys(rdev,
4407 info->attrs[NL80211_ATTR_KEYS]);
4408 if (IS_ERR(connkeys))
4409 return PTR_ERR(connkeys);
4410 }
4411
4412 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
4413 if (err)
4414 kfree(connkeys);
4415 return err;
4416}
In wl_cfg80211.c
1758static s32
1759wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
1760 struct cfg80211_ibss_params *params)
1761{
1762 struct wl_priv *wl = wiphy_priv(wiphy);
1763 struct cfg80211_bss *bss;
1764 struct ieee80211_channel *chan;
1765 struct wl_join_params join_params;
1766 struct cfg80211_ssid ssid;
1767 s32 scan_retry = 0;
1768 s32 err = 0;
1769 bool rollback_lock = false;
1770
1771 WL_TRACE(("In\n"));
1772 CHECK_SYS_UP(wl);
1773 if (params->bssid) { // remove the ibss-blocking code
1774 WL_ERR(("Invalid bssid\n"));
1775 return -EOPNOTSUPP;
1776 }
1777 bss = cfg80211_get_ibss(wiphy, NULL, params->ssid, params->ssid_len);
1778 if (!bss) {
1779 memcpy(ssid.ssid, params->ssid, params->ssid_len);
1780 ssid.ssid_len = params->ssid_len;
1781 do {
1782 if (unlikely
1783 (__wl_cfg80211_scan(wiphy, dev, NULL, &ssid) ==
1784 -EBUSY)) {
1785 wl_delay(150);
1786 } else {
1787 break;
1788 }
1789 } while (++scan_retry < WL_SCAN_RETRY_MAX);
1790 /* to allow scan_inform to propagate to cfg80211 plane */
1791 if (rtnl_is_locked()) {
1792 rtnl_unlock();
1793 rollback_lock = true;
1794 }
1795
1796 /* wait 4 secons till scan done.... */
1797 schedule_timeout_interruptible(4 * HZ);
1798 if (rollback_lock)
1799 rtnl_lock();
1800 bss = cfg80211_get_ibss(wiphy, NULL,
1801 params->ssid, params->ssid_len);
1802 }
1803 if (bss) {
1804 wl->ibss_starter = false;
1805 WL_DBG(("Found IBSS\n"));
1806 } else {
1807 wl->ibss_starter = true;
1808 }
1809 chan = params->channel;
1810 if (chan)
1811 wl->channel = ieee80211_frequency_to_channel(chan->center_freq);
1812 /*
1813 * Join with specific BSSID and cached SSID
1814 * If SSID is zero join based on BSSID only
1815 */
1816 memset(&join_params, 0, sizeof(join_params));
1817 memcpy((void *)join_params.ssid.SSID, (void *)params->ssid,
1818 params->ssid_len);
1819 join_params.ssid.SSID_len = htod32(params->ssid_len);
1820 if (params->bssid)
1821 memcpy(&join_params.params.bssid, params->bssid,
1822 ETHER_ADDR_LEN);
1823 else
1824 memset(&join_params.params.bssid, 0, ETHER_ADDR_LEN);
1825
1826 err = wldev_ioctl(dev, WLC_SET_SSID, &join_params,
1827 sizeof(join_params), false);
1828 if (unlikely(err)) {
1829 WL_ERR(("Error (%d)\n", err));
1830 return err;
1831 }
1832 return err;
1833}
发送给dhd固件,err是0,表发送成功;
WLC_SET_SSID的结果以异步形式由固件发送回来。此时收到的WLC_E_SET_SSID的status是WLC_E_STATUS_NO_NETWORKS。
设置WLC_SET_INFRA后,修改join_ibss相关代码,可以收到WLC_E_SET_SSID with status WLC_E_STATUS_SUCCESSFUL,连接上IBSS。
驱动中在关联AP成功时收到的主要是WLC_E_LINK、WLC_E_JOIN和WLC_E_SET_SSID,会通过event_handler处理或通知wpa_supplicant。其实只有收到WLC_E_JOIN事件时才表示真正join ibss或connect成功。
SME & MLME & PLME
Userspace SME uses kernel providing MLME to do mac layer management.
END