【转载】Android WiFi 架构总览(模块及接口)

原文地址:https://blog.csdn.net/xusiwei1236/article/details/48495485

Android WiFi 架构总览

本文介绍Android源码项目(AOSP)中WiFi功能的软件架构及各个模块(可执行文件、动态链接库)间的接口。

SDK API

Android SDK为开发者提供了WiFi编程接口,使用起来非常方便。

相关包: 
android.net.wifi(写App时只需import该包,即可使用WiFi相关功能)

主要相关类: 
WifiManager WIFI编程入口,WIFI的多数功能都以该类的方法的形式提供 
WifiInfo 用于描述WIFI连接的状态 
ScanResult 用于描述一个AP,如SSID,信号强度,安全方式等

Overview

下图基展示了Android系统WIFI模块的架构(当然,这只是软件的控制命令部分,数据部分直接通过kernel与网络子系统、socket API交互)。 
【转载】Android WiFi 架构总览(模块及接口)
(PS:一图胜千言,虽然用ppt画起来费劲) 
WifiManager是管理所有Wifi连接的基本API,可以通过: 
android.content.Context.getSystemService(Context.WIFI_SERVICE) 
得到它的实例。

具体 IPC(Inter-Process communication)

App & system_server(WifiManager & WifiService)

如果说Binder是连接 App 和 system_server 的桥梁,那么WifiManager和WiFiService就是桥梁的两头。

framework代码上和wifi相关的package位于: 
frameworks/base/wifi/java(WIFI相关的一些包) 
frameworks/base/services/java(各种Service,WIFI相关包为:com.android.server.wifi)

frameworks代码中,和wifi相关的几个类的关系如下:

  • WifiService继承自IWifiManager.Stub;
  • IWifiManager.Stub又继承自Binder,同时实现了IWifiManager接口;
  • WifiManager.Stu.proxy也实现了IWifiManager接口;

如图: 
【转载】Android WiFi 架构总览(模块及接口)

其中,IWifiManager, IWifiManager.Stub, IWifiManager.Stub.Proxy都由IWifiManger.aidl生成; 
aidl自动生成相关的java代码,简化了用Binder实现RPC的过程。 
IWifiManager.Stub.Proxy,WifiManager,BinberProxy用于客户端(App进程); 
而IWifiManager.Stub,WifiService,Binder用于服务端(SystemServer进程)。

App 与 system_server 通过Binder通信,但Binder本身只实现了IPC,即类似socket通信的能力。而App端的WifiManager和system_server端的WifiService及Binder等类共同实现了RPC(remote procedure call)。

WifiManager只是系统为app提供的接口。Context.getSystemService(Context.WIFI_SERVICE) 
返回的实际对象类型是IWifiManager.Stub.Proxy。 
IWifiManager.Stub.Proxy的实例是位于App端的一个代理,代理象IWifiManager.Stub.Proxy 
将WifiManager方法的参数序列化到Parcel,再经Binder发送给system_server进程。

system_server内的WifiService收App传来的WifiManager调用,完成实际工作。 
这样,实际和下层通信的工作就由App转移到了system_server进程(WifiService对象)。

WifiStateMachine

另外,可以看到 WifiStateMachine 是wifi功能的枢纽,几种不同模式下的控制流程都经它流下。 
当WIFI处在STA模式(或P2P模式)时,通过WifiNative与wpa_supplicant交互。 
WifiNative定义了几个Native方法:

 1     public native static boolean setMaxTxPower(int txpower, boolean sapRunning);
 2 
 3     public native static boolean loadDriver();
 4 
 5     public native static boolean isDriverLoaded();
 6 
 7     public native static boolean unloadDriver();
 8 
 9     public native static boolean startSupplicant(boolean p2pSupported, int firstScanDelay);
10 
11     /* Sends a kill signal to supplicant. To be used when we have lost connection
12        or when the supplicant is hung */
13     public native static boolean killSupplicant(boolean p2pSupported);
14 
15     private native boolean connectToSupplicantNative();
16 
17     private native void closeSupplicantConnectionNative();
18 
19     /**
20      * Wait for the supplicant to send an event, returning the event string.
21      * @return the event string sent by the supplicant.
22      */
23     private native String waitForEventNative();
24 
25     private native boolean doBooleanCommandNative(String command);
26 
27     private native int doIntCommandNative(String command);
28 
29     private native String doStringCommandNative(String command);

 

当WIFI处在AP模式。通过NetworkManagementService与netd交互,具体是通过LocalSocket(Framework封装的UNIX域socket)与netd进程通信。 
比如,NetworkManagementService.startAccessPoint方法:

 1     @Override
 2     public void startAccessPoint(
 3             WifiConfiguration wifiConfig, String wlanIface) {
 4         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 5         try {
 6             wifiFirmwareReload(wlanIface, "AP");
 7             if (wifiConfig == null) {
 8                 mConnector.execute("softap", "set", wlanIface); // 向 netd 发送控制命令
 9             } else {
10                 mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
11                                    wifiConfig.hiddenSSID ? "hidden" : "broadcast",
12                                    "1", getSecurityType(wifiConfig),
13                                    new SensitiveArg(wifiConfig.preSharedKey));
14             }
15             mConnector.execute("softap", "startap");
16         } catch (NativeDaemonConnectorException e) {
17             throw e.rethrowAsParcelableException();
18         }
19     }

 

WifiNative

从功能上来说,WifiNative是system_server 和 wpa_supplicant 对话的窗口,实际上主要依靠wpa_supplicant项目编译出来的动态库libwpa_client.so。

WifiNative的几个native方法的具体实现在: 
frameworks/base/core/jni/android_net_wifi_WifiNative.cpp

WifiNative的native方法的实现:

 1 static JNINativeMethod gWifiMethods[] = {
 2     /* name, signature, funcPtr */
 3     { "loadDriver", "()Z",  (void *)android_net_wifi_loadDriver },
 4     { "isDriverLoaded", "()Z",  (void *)android_net_wifi_isDriverLoaded },
 5     { "unloadDriver", "()Z",  (void *)android_net_wifi_unloadDriver },
 6     { "startSupplicant", "(ZI)Z",  (void *)android_net_wifi_startSupplicant },
 7     { "killSupplicant", "(Z)Z",  (void *)android_net_wifi_killSupplicant },
 8     { "connectToSupplicantNative", "()Z", (void *)android_net_wifi_connectToSupplicant },
 9     { "closeSupplicantConnectionNative", "()V", (void *)android_net_wifi_closeSupplicantConnection },
10     { "waitForEventNative", "()Ljava/lang/String;", (void*)android_net_wifi_waitForEvent },
11     { "doBooleanCommandNative", "(Ljava/lang/String;)Z", (void*)android_net_wifi_doBooleanCommand },
12     { "doIntCommandNative", "(Ljava/lang/String;)I", (void*)android_net_wifi_doIntCommand },
13     { "doStringCommandNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*) android_net_wifi_doStringCommand },
14     { "setMaxTxPower", "(IZ)Z",  (void *)android_net_wifi_setMaxTxPower },
15 };

 

android_net_wifi_WifiNative.cpp 并没有做多少实际工作,大多是直接调用 wifi.h wifi_maxtxpower.h 定义的函数: 
wifi.h 的具体实现在 hardware/libhardware_legacy/wifi/wifi.c (会被编译为 libhardware_legacy.so) 
wifi_maxtxpower.h 的具体实现在 hardware/qcom/wlan/libmaxtxpower/wifi_maxtxpower.c (会被编译为 libmaxtxpower.so) 
所以native代码依赖libhardware_legacy.so模块、libmaxtxpower.so模块。

WIFI HAL

实现SystemServer与wpa_supplicant(hostapd)通信的,即Wifi HAL。 
Wifi HAL封装了UNIX域socket,SystemServer通过UNIX域socket与wpa_supplicant(hostapd) 
通信;SystemServer发送的消息和wpa_supplicant响应的消息都是为ASCII字符串。

Wifi HAL代码主要分布在: 
hardware/libhardware_legacy/wifi 
hardware/qcom/wlan/libmaxtxpower

分别被编译为: 
hardware/libhardware_legacy/wifi -> libhardware_legacy.so 
hardware/qcom/wlan/libmaxtxpower -> libmaxtxpower.so

wifi.h定义了WIFI HAL的接口,具体函数有:

 1 // 驱动相关:
 2 int wifi_load_driver();
 3 int wifi_unload_driver();
 4 int is_wifi_driver_loaded();
 5 
 6 // supplicant相关:
 7 int wifi_start_supplicant(int p2pSupported, int first_scan_delay);
 8 int wifi_stop_supplicant(int p2pSupported);
 9 int wifi_connect_to_supplicant();
10 void wifi_close_supplicant_connection();
11 
12 // 等待WIFI事发生,该函数会阻塞当前调用,直到有wifi事件发生时,返回一个表示wifi事件的字符
13 int wifi_wait_for_event(char *buf, size_t len);
14 
15 // 向wifi驱动发一个命,(多数功能经该函数向下层发命令)
16 int wifi_command(const char *command, char *reply, size_t *reply_len);
17 
18 // 发起一dhcp请求
19 int do_dhcp_request(int *ipaddr, int *gateway, int *mask,
20                    int *dns1, int *dns2, int *server, int *lease);
21 
22 // 返回一个do_dhcp_request()的错误字符串
23 const char *get_dhcp_error_string();
24 
25 #define WIFI_GET_FW_PATH_STA    0
26 #define WIFI_GET_FW_PATH_AP     1
27 #define WIFI_GET_FW_PATH_P2P    2
28 
29 // 返一个请的firmware路径
30 const char *wifi_get_fw_path(int fw_type);
31 
32 // 为wlan驱动改变firmware路径
33 int wifi_change_fw_path(const char *fwpath);
34 
35 #define WIFI_ENTROPY_FILE   "/data/misc/wifi/entropy.bin"
36 
37 int ensure_entropy_file_exists();

 

wifi_maxtxpower.h 只定义了一个函数:

int set_max_tx_power(int power, int sap_running);
  • 1

android_net_wifi_WifiNative.cpp 调用了这些函数。 
wifi.c调用了wpa_ctrl.h定义的一些函数,而wpa_ctrl.h中的函数 
在external/wpa_supplicant_8 项目实现,并被编译为libwpa_client.so, 
详见external/wpa_supplicant_8/Android.mk。

wpa_supplicant(hostapd)

代码位于: 
external/wpa_supplicant_8 
该项目内包含两个互相相关的开源项目wpa_supplicant和hostapd,它们将会生成两个可执行文件: 
wpa_supplicant和hostapd,分别为STA模式和AP模式时的守护进程。

除此之外,还会生成用于测试的wpa_cli,hostapd_cli, 
以及WIFI HAL依赖的wpa_client.so,具体可以到Android.mk中找到。

wpa_supplicant源码结构和hostapd类似,下面只介绍wpa_supplicant。

wpa_ctrl.h 定义了与wpa_supplicant(或hostapd)进程通信的接口:

 1 struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);
 2 
 3 // Close a control interface to wpa_supplicant/hostapd
 4 void wpa_ctrl_close(struct wpa_ctrl *ctrl);
 5 
 6 // Send a command to wpa_supplicant/hostapd
 7 int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
 8              char *reply, size_t *reply_len,
 9              void (*msg_cb)(char *msg, size_t len));
10 
11 // Register as an event monitor for the control interface
12 int wpa_ctrl_attach(struct wpa_ctrl *ctrl);
13 
14 // Unregister event monitor from the control interface
15 int wpa_ctrl_detach(struct wpa_ctrl *ctrl);
16 
17 // Receive a pending control interface message
18 int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);
19 
20 // Check whether there are pending event messages
21 int wpa_ctrl_pending(struct wpa_ctrl *ctrl);
22 
23 // Get file descriptor used by the control interface
24 int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
25 
26 char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl);

 

wpa_ctrl_open 创建一个UNIX域socket 与 wpa_supplicant(或hostapd)进程相连, 
wpa_ctrl_close 用于关闭wpa_ctrl_open创建的连接, 
wpa_ctrl_request 用于向wpa_supplicant/hostapd发送控制命令,并阻塞, 
直到wpa_supplicant/hostapd返回命令响应。控制命令和相应都是ASCII字符串。

wpa_ctrl.h除了声明了这些函数外,还定义了wpa_supplicant/hostapd的一些消息,这里没有详细列出。

源码中wpa_supplicant_global_ctrl_iface_receive 
负责分派上层发来的控制命令,进而调用具体处理函数:

 1  "ATTACH" -> wpa_supplicant_ctrl_iface_attach
 2  "DETACH" -> wpa_supplicant_ctrl_iface_detach
 3   else -> wpa_supplicant_global_ctrl_iface_process:
 4     "IFNAME="(prefix) // 多数控制命令以IFNAME=开头
 5         -> wpas_global_ctrl_iface_ifname
 6             -> wpa_supplicant_ctrl_iface_process # "IFNAME="开头的命令的处理
 7     wpas_global_ctrl_iface_redir 
 8         -> wpas_global_ctrl_iface_redir_p2p
 9             -> wpa_supplicant_ctrl_iface_process # "IFNAME="开头的命令的处理
10         -> wpas_global_ctrl_iface_redir_wfd
11             -> wpa_supplicant_ctrl_iface_process # "IFNAME="开头的命令的处理
12     "PING" -> "PONG"
13     "INTERFACE_ADD" -> wpa_supplicant_global_iface_add
14     "INTERFACE_REMOVE" -> wpa_supplicant_global_iface_remove
15     "INTERFACE_LIST" -> wpa_supplicant_global_iface_list
16     "INTERFACES" -> wpa_supplicant_global_iface_interfaces
17     "TERMINATE" -> wpa_supplicant_terminate_proc
18     "SUSPEND" -> wpas_notify_suspend
19     "RESUME" -> wpas_notify_resume
20     "SET" -> wpas_global_ctrl_iface_set
21     "SAVE_CONFIG" -> wpas_global_ctrl_iface_save_config
22     "STATUS" -> wpas_global_ctrl_iface_status

 

该函数在wpa_supplicant目录下的 
ctrl_iface_unix.cctrl_iface_udp.c内都有实现,可由该目录下的android.config切换 
(android.config被Android.mk包含,具体参见Android.mk)

wpa_supplicant与内核通信

wpa_supplicant的运行模型是单进程单线程的 Reactor(IO multiplexing)。wpa_supplicant通过NETLINK socket与内核通信。

wpa_supplicant项目支持多种驱动编程接口,在Android上使用的是nl80211;

nl80211是新的802.11netlink接口公共头,与cfg80211一同组成了Wireless-Extensions的替代方案。 
cfg80211是Linux 802.11配置API, nl80211用于配置cfg80211设备,同时用于内核到用户空间的通信。

实际使用nl80211时,只需要在程序中包含头文件

wireless module(in kernel)

代码位于: 
kernel/net/wireless

nl80211.c 中的 nl80211_init 使用genl_register_family_with_ops 注册了响应应用程序的 
struct genl_ops nl80211_ops[], 该数组定义了响应NETLINK消息的函数。

而 nl80211_init 在 cfg80211_init 内被调用,cfg80211_init是被subsys_initcall注册的 
子系统初始化程序,被编译为cfg80211.ko。

nl80211_ops[] 节选:

 1 static struct genl_ops nl80211_ops[] = {
 2     // ...
 3     {
 4         .cmd = NL80211_CMD_TRIGGER_SCAN,
 5         .doit = nl80211_trigger_scan,
 6         .policy = nl80211_policy,
 7         .flags = GENL_ADMIN_PERM,
 8         .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
 9                   NL80211_FLAG_NEED_RTNL,
10     },
11     {
12         .cmd = NL80211_CMD_GET_SCAN,
13         .policy = nl80211_policy,
14         .dumpit = nl80211_dump_scan,
15     },
16     {
17         .cmd = NL80211_CMD_START_SCHED_SCAN,
18         .doit = nl80211_start_sched_scan,
19         .policy = nl80211_policy,
20         .flags = GENL_ADMIN_PERM,
21         .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
22                   NL80211_FLAG_NEED_RTNL,
23     },
24     {
25         .cmd = NL80211_CMD_STOP_SCHED_SCAN,
26         .doit = nl80211_stop_sched_scan,
27         .policy = nl80211_policy,
28         .flags = GENL_ADMIN_PERM,
29         .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
30                   NL80211_FLAG_NEED_RTNL,
31     },
32 
33     // ...
34 };

 

wlan driver

代码位于: 
vendor/qcom/opensource/wlan/prima

模块初始化(module_init),模块退出(module_exit): 
CORE/HDD/src/wlan_hdd_main.c

模型: 
多线程 + 队列

创建内核线程: 
CORE/VOSS/src/vos_sched.c 的 vos_sched_open()

线程任务(vos_sched.c): 
* VosMcThread() - The VOSS Main Controller thread 
* VosWdThread() - The VOSS Watchdog thread 
* VosTXThread() - The VOSS Main Tx thread 
* VosRXThread() - The VOSS Main Rx thread

线程环境(context) (vos_sched.h):

 1 typedef struct _VosSchedContext
 2 {
 3   /* Place holder to the VOSS Context */ 
 4    v_PVOID_t           pVContext; 
 5 
 6   /* WDA Message queue on the Main thread*/
 7    VosMqType           wdaMcMq;
 8 
 9    /* PE Message queue on the Main thread*/
10    VosMqType           peMcMq;
11 
12    /* SME Message queue on the Main thread*/
13    VosMqType           smeMcMq;
14 
15    /* TL Message queue on the Main thread */
16    VosMqType           tlMcMq;
17 
18    /* SYS Message queue on the Main thread */
19    VosMqType           sysMcMq;
20 
21   /* WDI Message queue on the Main thread*/
22    VosMqType           wdiMcMq;
23 
24    /* WDI Message queue on the Tx Thread*/
25    VosMqType           wdiTxMq;
26 
27    /* WDI Message queue on the Rx Thread*/
28    VosMqType           wdiRxMq;
29 
30    /* TL Message queue on the Tx thread */
31    VosMqType           tlTxMq;
32 
33    /* TL Message queue on the Rx thread */
34    VosMqType           tlRxMq;
35 
36    /* SYS Message queue on the Tx thread */
37    VosMqType           sysTxMq;
38 
39    VosMqType           sysRxMq;
40 
41     // ...
42 
43    struct task_struct* McThread;
44 
45    /* TX Thread handle */
46 
47    struct task_struct*   TxThread;
48 
49    /* RX Thread handle */
50    struct task_struct*   RxThread;
51 
52     // ...
53 } VosSchedContext, *pVosSchedContext;

 

高通资料: 
80-Y0513-1_G_QCA_WCN36x0_Software_Architecture.pdf 
chapter: Android WLAN Host Software Architecture

SCAN过程跟踪

App

 1 WifiManager wifiManager = (WifiManager)Context.getService(Contex.WIFI_SERVICE);
 2 wifiManager.startScan();
 3 
 4 //wifiManager --> IWifiManager.Stub.Proxy
 5 
 6 IWifiManager.Stub.Proxy implements android.net.wifi.IWifiManager
 7 
 8 wifiManager.startScan() 
 9 -> IWifiManager.Stub.Proxy.startScan(WorkSource=null);
10 -> BinderProxy.transact(Stub.TRANSACTION_startScan, _data, _reply, 0);

 

systerm_server

wifi –> WifiService

 1 WifiService extends IWifiManager.Stub
 2 
 3 IWifiManager.Stub extends android.os.Binder 
 4     implements android.net.wifi.IWifiManager
 5 
 6 -> IWifiManager.Stub.onTransact(int code, Parcel data, Parcel reply, int flags);
 7     case TRANSACTION_startScan:
 8 -> WifiService.startScan(WorkSource workSource);
 9 -> WifiStateMachine.startScan(int callingUid, WorkSource workSource);
10 -> StateMachine.sendMessage(CMD_START_SCAN, callingUid, 0, workSource);
11     case CMD_START_SCAN:
12 ->  handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
13 ->  startScanNative(type, freqs)
14 -> WifiNative.scan(type, freqs)
15 ->  doBooleanCommand("SCAN ..."); // AF_UNIX socket, send to wpa_supplicant

 

wpa_supplicant

1 external/wpa_supplicant_8/wpa_supplicant$ grep -nr "\"SCAN " .
2 ./ChangeLog:197:      - "SCAN freq=<freq list>" can be used to specify which channels are
3 ./ChangeLog:199:      - "SCAN passive=1" can be used to request a passive scan (no Probe
4 ./ChangeLog:201:      - "SCAN use_id" can be used to request a scan id to be returned and
5 ./ChangeLog:203:      - "SCAN only_new=1" can be used to request the driver/cfg80211 to
6 ./ctrl_iface.c:6986:    } else if (os_strncmp(buf, "SCAN ", 5) == 0) {
7 ./src/drivers/driver_test.c:1289:   ret = os_snprintf(pos, end - pos, "SCAN " MACSTR,
8 ./src/drivers/driver_test.c:1994:   } else if (os_strncmp(buf, "SCAN ", 5) == 0) {
9 ./ctrl_iface.c:6986:    } else if (os_strncmp(buf, "SCAN ", 5) == 0) {

 

refer to ./ctrl_iface.c:6986

1     } else if (os_strcmp(buf, "SCAN") == 0) {
2         wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len);
3     } else if (os_strncmp(buf, "SCAN ", 5) == 0) {
4         wpas_ctrl_scan(wpa_s, buf + 5, reply, reply_size, &reply_len);

 

wpas_ctrl_scan -> wpa_supplicant_req_scan

1     int res = eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s,
2                     NULL);
3     if (res == 1) {
4         wpa_dbg(wpa_s, MSG_DEBUG, "Rescheduling scan request: %d.%06d sec",
5             sec, usec);
6     }

 

wpa_supplicant_scan -> wpa_supplicant_trigger_scan

1 -> radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx)

 

wpas_trigger_scan_cb -> wpa_drv_scan

1 static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s,
2                    struct wpa_driver_scan_params *params)
3 {
4     if (wpa_s->driver->scan2) // callback
5         return wpa_s->driver->scan2(wpa_s->drv_priv, params);
6     return -1;
7 }

 

grep scan2 callback

1 external/wpa_supplicant_8/wpa_supplicant$ grep -nr "scan2\s*=" .
2 ./src/drivers/driver_wext.c:2401:   .scan2 = wpa_driver_wext_scan,
3 ./src/drivers/driver_privsep.c:726: .scan2 = wpa_driver_privsep_scan,
4 ./src/drivers/driver_test.c:2677:   .scan2 = wpa_driver_test_scan,
5 ./src/drivers/driver_bsd.c:1618:    .scan2          = wpa_driver_bsd_scan,
6 ./src/drivers/driver_nl80211.c:12612:   .scan2 = driver_nl80211_scan2,
7 ./src/drivers/driver_ndis.c:3217:   wpa_driver_ndis_ops.scan2 = wpa_driver_ndis_scan;

 

refer to ./src/drivers/driver_nl80211.c:12612

driver_nl80211_scan2 -> wpa_driver_nl80211_scan

1     msg = nl80211_scan_common(drv, NL80211_CMD_TRIGGER_SCAN, params,
2                   bss->wdev_id_set ? &bss->wdev_id : NULL);
3     if (!msg)
4         return -1;

 

use NL80211_CMD_TRIGGER_SCAN talk with kernel(cfg80211.ko)

kernel

grep NL80211_CMD_TRIGGER_SCAN in kernel source:

1 kernel$ cgrep NL80211_CMD_TRIGGER_SCAN
2 ./net/wireless/nl80211.c:9053:      .cmd = NL80211_CMD_TRIGGER_SCAN,
3 ./net/wireless/nl80211.c:9605:                NL80211_CMD_TRIGGER_SCAN) < 0) {
4 ./include/uapi/linux/nl80211.h:255: *   option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN,
5 ./include/uapi/linux/nl80211.h:260: * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
6 ./include/uapi/linux/nl80211.h:759: NL80211_CMD_TRIGGER_SCAN,
7 ./include/uapi/linux/nl80211.h:1362: *  This attribute is used with %NL80211_CMD_TRIGGER_SCAN and
8 ./include/uapi/linux/nl80211.h:3863: * of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN

 

refer to net/wireless/nl80211.c:9053

 1 static struct genl_ops nl80211_ops[] = {
 2 
 3 // ... ...
 4 
 5     {
 6         .cmd = NL80211_CMD_TRIGGER_SCAN,
 7         .doit = nl80211_trigger_scan,
 8         .policy = nl80211_policy,
 9         .flags = GENL_ADMIN_PERM,
10         .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
11                   NL80211_FLAG_NEED_RTNL,
12     },
13 
14 // ... ...
15 };

 

nl80211_trigger_scan -> rdev_scan(rdev, request);

1 static inline int rdev_scan(struct cfg80211_registered_device *rdev,
2                 struct cfg80211_scan_request *request)
3 {
4     int ret;
5     trace_rdev_scan(&rdev->wiphy, request);
6     ret = rdev->ops->scan(&rdev->wiphy, request); // callback
7     trace_rdev_return_int(&rdev->wiphy, ret);
8     return ret;
9 }

转载注明请出处(https://blog.csdn.net/xusiwei1236),勿做商用! 
欢迎评论或email(xusiwei1236@163.com)交流观点

driver

参考高通文档

 

【转载】Android WiFi 架构总览(模块及接口)

上一篇:Spring的beanFactory与ApplicationContext区别


下一篇:手动获取spring的ApplicationContext和bean对象