原文地址: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交互)。
(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接口;
如图:
其中,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.c
和ctrl_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
参考高通文档