Android Wifi work station Framework and Architecture

来源:互联网 发布:随心所欲动作数据 编辑:程序博客网 时间:2024/05/26 02:19

Android Wifi work station Framework and Architecture

with wpa_supplicant 0.8.X, BCM4329.

转载请注明出处。

  1. Settings/Wifi UI part structure

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创建的时候会建立和WifiServiceAsyncChannel的连接,WifiService同样会建立一个AsyncChannelHandler来处理来自这个Channel的命令消息。AsyncChannel的所谓异步主要用来传递比较耗时的操作并把结果返回给原请求者。AsyncChannel两端分别有一个MessageHandlersrcHandler请求destHandlerdestHandler把结果发回给srcHandler,主要是用于处理比较耗时的操作且在WifiManager中处理返回结果。

严格来说,在WifiServiceInterface方法之外再做出一个通道提供额外的方法并不是一个什么好的设计。命令是可以通过同步接口发送给WifiService的,但是WifiService的处理结果如果通过broadcastintentWifiManager,则又解耦的过度;如果WifiManager实现一个binderevent 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;

        }

    }

可以看出对ADHOCAP,隐藏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时,处理代码会调用到mWifiEnablersetSwitch方法,然后会通知其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进行连接。

另外的两个类的解释:

WifiConfigControllerMVC中的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.

WifiManagerWifiService的客户端接口的封装类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.

 

  1. WifiService Part structure

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,在同一个线程中,所以mAsyncServiceHandlermWifiStateMachineHandlerWifiStateMachine.mSmhandler是使用同一个looper,运行在wifiThread中。WifiStateMachine籍由StateMachine具备有单独looper在单独线程运行的能力。

WifiSerivce作为一个serviceWifiService.javaframeworks/base/services/java/com/android/server/目录下。但其实现使用的WifiStateMachineframeworks/base/wifi/java/android/net/wifi/目录下。

WifiStateMachine状态机使用WifiNative wpa_ctrlwpa_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. WifiStateMachinehierarchy如下图所示:

下面考察初始状态到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_ENABLINGCMD_START_SUPPLICANT两个命令。

DriverUnloadedState状态下,处理CMD_LOAD_DRIVER命令,向WifiP2pService发送WIFI_ENABLE_PENDING命令,也就是说通知WifiP2pService需要退出要启用Wifi了,因为WifiServiceWifiP2pService同一时刻只能使用一个,然后转到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_CODECMD_SET_FREQUENCY_BAND设置国家代号和频段,在本状态处理这两个命令;这两个命令是要发送给芯片的,所以在固件和驱动加载好后发送。设置好频段后,就向自身发送消息CMD_START_SCAN启动一次active scan。然后经由状态ConnectModeState转换状态至DisconnectedState

DisconnectedState下收到扫描结果WifiMonitor.SCAN_RESULTS_EVENT,消息路由给父状态SupplicantStartedStateprocessMessage进行处理,获取扫描结果并保存。在此状态下SupplicantStartedState处理与AP配置相关的命令,ConnectModeState处理与AP连接相关的命令。例如可以配置某个AP,然后请求connectNetwork,对于CMD_CONNECT_NETWORK,会使用Connect相关状态ConnectModeState.processMessage()处理,启动AP关联,然后转换状态到DisconnectingState与旧AP断开连接与新AP建立连接。

DisconnectingState状态收到关联到新AP成功消息WifiMonitor.NETWORK_CONNECTION_EVENT后,使用ConnectModeState.processMessage处理,记录关联上的SSIDBSSID,获取AP信号强度和速度,广播消息,转换状态至ConnectingState进行IP获取。

ConnectingState.enter()中使用静态IPDHCP获取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,一个用于控制一个用于向请求者返回异步响应,所以决定了WifiStateMachinewpa_supplicant之间的软件结构是有一个接收线程阻塞在读端口上,而写端口时根据速率匹配看是否需要单独的发送进程。

WifiStateMachinewpa_supplicant之间的数据通信量是很小的,所以不需要单独的发送进程,只需要一个常接收阻塞的接收线程即可,这个线程就是WifiMonitorWifiMonitor就是个ReceiverThread加消息协议转换。WifiMonitor在使用unix domain socket ctrl interface时是pollsocket上,有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加载

bcm4330wifi驱动可能是bcmdhd.sodhd.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.rcwpa_cli使用wpa_ctrl_opensocket connect连接这个服务名。Android修改了wpa_supplicant打开socket的代码,是在init.Manufacture.rc中在init阶段打开,wpa_supplicantwpa_supplicant_ctrl_iface_init()时查找该socket直接使用。注意由于以下没有-g选项,所以wpa_supplicant_global_ctrl_iface_init()时的params.ctrl_interface is NULL。选项中传入的interfacewpa_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,不能acceptrecvfrom就可以得到该client socket的地址可以知道socketpid,另外本地可以使用文件名chown/chmod

wpa_cli使用wpa_ctrl_open创建两个socketbind本地文件名,connect wpa_supplicant接口名(对于DGRAMconnect并没有真正的作用,仅是做peer addr绑定)wpa_supplicant不需要accept直接在其socketrecvfrom;当wpa _cli wpa_ctrl_attach其中一个时,从该socket读出”ATTACH”/”DETACH”,然后可以这个socket以后是monitor socket,因此以后不必在从此socket接收数据。

wpa_cli为了在wpa_ctrl_request出错时,退出recv(monitor_socket),用了socketpair中的读端socketmonitor_socket一起select,从而出错时正常退出。

具体参见wpa_ctrl.c文件。

wpa_supplicantdriverfirmware不是手动加载的时候,直接打开WifiEnabler,则已打开的wpa_ctrl的事件也会输出到wpa_cliwpa_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_supplicantlog输出到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文件中参数。可覆盖的主要是driverctrl_interface

 

  1. wpa_supplicant结构

总体结构。UML struct wpa_supplicant struct wpa_interface。具体组织结构和数据结构参见developers guidehttp://w1.fi/wpa_supplicant/wpa_supplicant-devel.pdf

wpa_supplicant使用eloop接收unix domain socket传来的命令,使用libnlnl80211交互。

接收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通过libnlnl80211网卡驱动通信,详述driver_nl80211kernel通信的接口。主要使用了一个普通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接收,接收的eventnl80211 scannl80211 mlmenl80211 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));

Eventmonitor/dev/rfkillrecv_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上,该socketrecv函数是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_SCANkernel驱动。

wpa_supplicant_scan()会根据不同ap_scan做扫描或根据驱动关联结果发送关联event。大多数情况下ap_scan1trigger 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_resultspick 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_resultswpa_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),这是AuthenticateAssociate分步的情况。

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时, AUTHASSOCIATE分离,

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用于表示已经关联上APlog输出片段如下:

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帧事件,其余如datacontrol帧使用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

 

  1. Driver

Driver config部分主要由nl80211 thin layerlinux cfg80211vendor cfg80211部分组成,用户空间的NL80211_CMD发送给nl80211通过linux cfg80211转发或者直接发送给vendor cfg80211vendor cfg80211控制芯片,收到操作完成的通知后,通过linux cfg80211接口经过nl80211Multicast netlink socket发送通知给用户空间。

linux nl80211接口主要文件kernel/net/wireless/nl80211.c,向Generic Netlink注册了”nl80211” family和几个multicast group。除了nl80211kernel/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 packetTCPIP stack处理。Event帧则剥离ether_hdr后直接由dhd_wl_host_event()处理。

dhd_wl_host_event

  => wl_host_event处理host感兴趣的事件

  => wl_iw_event处理wext范围内事件

  => wl_cfg80211_eventcfg80211相关事件放入处理队列!

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}

 

下面描述scanjoin/connectcfg80211中的处理过程

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, &params);

 

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_cfg80211wl_cfg80211_change_virtual_iface()设置到IBSS模式时没有执行,需要patchWLC_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_ifmodewl_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固件,err0,表发送成功;

WLC_SET_SSID的结果以异步形式由固件发送回来。此时收到的WLC_E_SET_SSIDstatusWLC_E_STATUS_NO_NETWORKS

设置WLC_SET_INFRA后,修改join_ibss相关代码,可以收到WLC_E_SET_SSID with status WLC_E_STATUS_SUCCESSFUL,连接上IBSS

驱动中在关联AP成功时收到的主要是WLC_E_LINKWLC_E_JOINWLC_E_SET_SSID,会通过event_handler处理或通知wpa_supplicant。其实只有收到WLC_E_JOIN事件时才表示真正join ibssconnect成功。

SME & MLME & PLME

Userspace SME uses kernel providing MLME to do mac layer management.

END



http://blog.csdn.net/zirconsdu/article/details/8569193

0 0
原创粉丝点击