一、引言

上一篇博客介绍了MMC子系统和SDIO相关知识及架构,这一篇博客则分析一下安卓的WLAN架构及上层如何调用WLAN驱动的流程。

虽然我工作的开发环境是安卓系统,但由于我不是专业的安卓应用/框架开发工程师,所以对安卓层的一些代码并不是特别熟悉,只能大致梳理流程,不专业的地方请谅解。

开发环境:安卓9.0。

本文部分内容摘抄自网络,若有侵权,请联系删除。

二、基本概念

1、WiFi

WIreless-FIdelity,翻译成中文就是无线保真,英文简称WiFi。

2、WLAN

Wireless Local Area Networks, 无线局域网络。

3、WiFi和WLAN的关系

WiFi是实现WLAN的一种技术。

4、STA

Station, 类似于无线终端,sta本身并不接受无线的接入,它可以连接到AP,一般无线网卡即工作在该模式。

5、AP

Access Point,提供无线接入服务,允许其它无线设备接入,提供数据访问,一般的无线路由/网桥工作在该模式下。AP和AP之间允许相互连接。

三、安卓WLAN架构

1、应用框架(Application framework)

在应用程序框架级别是应用程序代码,它使用各种 android.net.wifi API与Wi-Fi框架和硬件进行交互。在内部,此代码通过Binder IPC机制调用Wi-Fi进程。

2、WiFi服务(Wi-Fi Service)

Wi-Fi服务在系统服务中运行,位于中 frameworks/opt/net/wifiWi-Fi服务通过HIDL与Wi-Fi HAL通信

有各种Wi-Fi服务:

  • Wi-Fi服务:控制Wi-Fi基础架构模式(STA和AP)的主要机制。
  • Wi-Fi P2P服务:管理Wi-Fi Direct模式(WiFi直连)。
  • Wi-Fi感知服务:管理Wi-Fi感知模式。
  • Wi-Fi RTT服务:管理IEEE 802.11mc FTM功能。

在Wi-Fi框架还包括一个独立的过程——wificond,位于system/connectivity/wificond。该wificond与Wi-Fi驱动程序通过标准流程进行通信nl80211的命令。

3、WiFi HAL

Wi-Fi框架具有三个Wi-Fi HAL表面(HIDL):

  • Vendor HAL:适用于Android的命令的HAL表面。HIDL文件位于中hardware/interfaces/wifi/1.x
  • Supplicant HAL:wpa_supplicant的HAL表面。HIDL文件位于中 hardware/interfaces/supplicant/1.x
  • Hostapd HAL:hostapd的HAL表面。HIDL文件位于中 hardware/interfaces/hostapd/1.x

3.1、Vendor HAL

供应商HAL提供特定于Android的命令。对于基础结构站(STA)和软AP(SAP)模式,它是可选的(不是必需的)。但是,对于Wi-Fi Aware和 Wi-Fi RTT服务是强制性的。

HIDL之前的版本(即Android 8.0之前的版本)Android使用了HAL机制,现在称为传统HAL。Android源代码当前使用在旧式HAL之上运行的填充程序提供HIDL的默认实现。

旧版HAL标头位于 hardware/libhardware_legacy/include/hardware_legacy/

基于旧式HAL的新式HAL实现位于hardware/interfaces/wifi/1.x/default

3.2、Supplicant HAL

Supplicant  HAL为wpa_supplicant守护程序提供了一个HIDL接口。

wpa_supplicant的作用是,用于控制无线连接。

wpa_supplicant源代码位于中 external/wpa_supplicant_8/wpa_supplicant。提供HIDL接口的wpa_supplicant代码位于hidl子目录中。

3.3、Hostapd HAL

Hostapd HAL为hostapd守护程序提供了HIDL接口。

hostapd能够使得无线网卡切换为master模式,模拟AP(通常可以认为是路由器)功能,也就是我们说的软AP(Soft AP)。

hostapd源代码位于中external/wpa_supplicant_8/hostapd。提供HIDL接口的hostapd代码位于hidl 子目录中。

四、架构解析

上一章梳理了一下安卓的WLAN框架,可能大家还有不明白的地方,这里,来做一下小小的总结。

框架第一层,属于应用层(WiFi Setting),这一层可以让普通的用户进行ui的界面操作,比如打开关闭WiFi,选择某个AP进行连接等。

框架第二层,属于服务层(WiFi Service),这一层是整个框架的核心部分,application层触发事件后,会调用这个service里面的方法来处理事件,包括加载驱动,开启wpa_supplicant,扫描AP都是调用这个service里面的方法实现。

框架第三层,属于接口层(WiFi Hidl),这一层将框架与HAL隔离,让安卓可以只升级框架而不修改HAL。简单理解,这一层就是提供接口让WiFi Service和WiFi HAL通信的一层。

框架第四层,属于硬件抽象层(WiFi Hal),这一层将安卓与kernel隔离。但在这里与其他驱动框架有所不同,hal层不直接与kernel的WiFi驱动进行通信,而是与wpa_supplicant或者hostapd这两个守护进程进行通信。

框架第五层,属于守护进程层(Wpa_supplicant/Hostapd),这一层接收hal层传送过来的命令和消息,然后接收到的命令和消息发送给WiFi驱动,wpa_supplicant向hal提供了WiFi的配置、连接、断开等接口。

框架第六层,属于内核驱动层(WiFi Driver)。

1、WiFi Service

安卓系统启动WiFi Service是在android/frameworks/base/services/java/com/android/server/SystemServer.java文件中启动。

代码片段如下:

// 必须先启动WiFi服务才能启动其他WiFi相关的内容.
traceBeginAndSlog("StartWifi");
mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
traceEnd();

2、WiFi Hidl

WiFi Hidl代码在android/hardware/interfaces/wifi/1.2/default。

HIDL 接口具有客户端和服务器实现:

- HIDL 接口的客户端实现是指通过在该接口上调用方法来使用该接口的代码。

- 服务器实现是指 HIDL 接口的实现,它可接收来自客户端的调用并返回结果(如有必要)。

在从libhardware HAL 转换为 HIDL HAL 的过程中,HAL 实现成为服务器,而调用 HAL 的进程则成为客户端。默认实现可提供直通和 Binder 化 HAL。

上图为HAL的几个发展历程,目前这个安卓版本所用的是第四种binderrized模式。框架和HAL之间通过HIDL接口实现通信。

以下的内容都是Vendor HAL的hidl,Supplicant  HAL和Hostapd HAL的hidl实现方式都类似,就不一一介绍了。

2.1、HIDL 服务器端

上面说了HIDL接口具有客户端和服务器端的实现,这里,就来介绍一下HIDL服务器端的编译。

编译代码内容如下:

### android.hardware.wifi daemon
include $(CLEAR_VARS)
LOCAL_MODULE := android.hardware.wifi@1.0-service
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_PROPRIETARY_MODULE := true
LOCAL_CPPFLAGS := -Wall -Werror -Wextra
LOCAL_SRC_FILES := \service.cpp
LOCAL_SHARED_LIBRARIES := \libbase \libcutils \libhidlbase \libhidltransport \liblog \libnl \libutils \libwifi-hal \libwifi-system-iface \android.hardware.wifi@1.0 \android.hardware.wifi@1.1 \android.hardware.wifi@1.2
LOCAL_STATIC_LIBRARIES := \ 89     android.hardware.wifi@1.0-service-lib
LOCAL_INIT_RC := android.hardware.wifi@1.0-service.rc
include $(BUILD_EXECUTABLE)

android.hardware.wifi@1.0-service就是我们编译生成的结果,WiFi Service层会通过这个服务来与我们的Wifi Hal进行通信。

注意看一下里面的这个lib(android.hardware.wifi@1.0-service-lib),这个lib就是支持各种WiFi功能的接口,比如sta啊,ap啊等等。

编译过程如下:

### android.hardware.wifi static library
include $(CLEAR_VARS)
LOCAL_MODULE := android.hardware.wifi@1.0-service-lib
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_PROPRIETARY_MODULE := true
LOCAL_CPPFLAGS := -Wall -Werror -Wextra
LOCAL_SRC_FILES := \hidl_struct_util.cpp \hidl_sync_util.cpp \ringbuffer.cpp \wifi.cpp \wifi_ap_iface.cpp \wifi_chip.cpp \wifi_feature_flags.cpp \wifi_legacy_hal.cpp \wifi_legacy_hal_stubs.cpp \wifi_mode_controller.cpp \wifi_nan_iface.cpp \wifi_p2p_iface.cpp \wifi_rtt_controller.cpp \wifi_sta_iface.cpp \wifi_status_util.cpp
LOCAL_SHARED_LIBRARIES := \libbase \libcutils \libhidlbase \libhidltransport \liblog \libnl \libutils \libwifi-hal \libwifi-system-iface \android.hardware.wifi@1.0 \android.hardware.wifi@1.1 \android.hardware.wifi@1.2
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
include $(BUILD_STATIC_LIBRARY)

可以看到SRC_FILE里面有很多cpp文件,比如wifi_sta_iface.cpp,这里面就有sta功能的一系列hidl接口。

2.2、启动android.hardware.wifi@1.0-service

启动android.hardware.wifi@1.0-service在android.hardware.wifi@1.0-service.rc文件中实现。

代码内容如下:

service vendor.wifi_hal_legacy /vendor/bin/hw/android.hardware.wifi@1.0-serviceclass halcapabilities NET_ADMIN NET_RAW SYS_MODULEuser wifigroup wifi gps root

3、WiFi Hal

WiFi Hal层代码路径:android/frameworks/opt/net/wifi/libwifi_hal。

WiFi Hal层代码编译之后会生成一个叫做libwifi_hal.so的文件。

五、调用流程

下面以打开WiFi为例,来简单介绍一下安卓系统从用户点击打开WiFi按钮到底层WiFi驱动的大致流程。

1、应用层(WiFi Setting)

1.1、android/packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java

@Override
public void onStart() {super.onStart();//创建一个WifiEnabler对象,实现开关WiFi功能mWifiEnabler = createWifiEnabler();if (mIsRestricted) {restrictUi();return;}onWifiStateChanged(mWifiManager.getWifiState());
}

在上述代码中,可以看到,这里调用了createWifiEnabler函数,创建了一个WifiEnabler对象。这个对象可以实现对WiFi的开关功能,它的实现就是1.2的WiFiEnabler.java。

1.2、android/packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java

public boolean onSwitchToggled(boolean isChecked) {if (!mWifiManager.setWifiEnabled(isChecked)) {mSwitchWidget.setEnabled(true);Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show();
}

当WiFi开关按钮状态发生变化时,onSwitchToggled这个函数会被调用。

可以看到,函数内部有这么一段代码——mWifiManager.setWifiEnabled(isChecked),这里就是开关WiFi的操作。

到这,应用层就结束了,因为mWifiManager这个对象是属于java框架层的。

2、java框架层

2.1、android/frameworks/base/wifi/java/android/net/wifi/WifiManager.java

public boolean setWifiEnabled(boolean enabled) {try {return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}

可以看到这里调用了mService的setWifiEnabled,显然,这里是调用到了WiFi Service层的东西了。

不过,java框架层想要与WiFi Service通信,需要跨进程,所以就需要用到aidl了。

2.2、android/frameworks/base/wifi/java/android/net/wifi/IWifiManager.aidl

boolean setWifiEnabled(String packageName, boolean enable);

aidl可以帮助WifiManager跨进程访问WifiService的接口。

3、WiFi Service层

3.1、android/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java

mWifiManager.setWifiEnabled通过aidl跨进程调用到了WifiServiceImpl.setWifiEnabled,其中WifiServiceImpl是WifiService的实现类。

@Override
public synchronized boolean setWifiEnabled(String packageName, boolean enable)throws RemoteException {……mWifiController.sendMessage(CMD_WIFI_TOGGLED);……
}

这里省略了大部分代码,我们只关注mWifiController.sendMessage这个调用。

3.2、android/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiController.java

WifiController,它是一个状态机。WifiController在WIfiServiceImpl的构造函数中初始化、并开始运行。

WifiController 和WifiStateMachine 不同,WifiStateMachine是一个复杂的状态机,它维护了Wifi的启动、扫描、连接、断开等多个状态。WifiController 是高级别的wifi状态机,因为它管理的状态是wifi开关,wifi热点开关等状态,只有在wifi开关等具体状态下,判断wifi处于启动扫描附近热点状态等才是有意义的。

WifiController状态机各状态关系:

可以看到,WifiController状态机初始状态为StaDisabledState。他会从初始状态开始处理从WiFi Service那里发过来的消息。

WifiController状态机在StaDisabledState状态下没做什么处理,我们看到DeviceActiveState状态。

class DeviceActiveState extends State {@Overridepublic void enter() {mWifiStateMachinePrime.enterClientMode();mWifiStateMachine.setHighPerfModeEnabled(false);}……
}

可以看到在DeviceActiveState状态下主要做了两个操作mWifiStateMachinePrime.enterClientMode()和mWifiStateMachine.setHighPerfModeEnabled(false),主要看mWifiStateMachinePrime.enterClientMode()。

3.3、android/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachinePrime.java

public void enterClientMode() {changeMode(ModeStateMachine.CMD_START_CLIENT_MODE);
}
private void changeMode(int newMode) {mModeStateMachine.sendMessage(newMode);
}

ModeStateMachine又是一个状态机,不过这个状态机比较简单只有三个状态,初始状态为WifiDisabledState。

ModeStateMachine状态机在WifiDisabledState并没有做太多事情,状态转换到ClientModeActiveState。

class ClientModeActiveState extends ModeActiveState {……ClientListener mListener;private class ClientListener implements ClientModeManager.Listener {@Override……}public void enter() {Log.d(TAG, "Entering ClientModeActiveState");mListener = new ClientListener();mManager = mWifiInjector.makeClientModeManager(mListener);mManager.start();mActiveModeManagers.add(mManager);updateBatteryStatsWifiState(true);}……
}

这里的mManager是ActiveModeManager,是个接口,这里的ClientModeManager实现了这个接口。我们继续走下去,去看ClientModeManager,主要看mManager.start()这个调用。

3.4、android/frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeManager.java

public void start() {mStateMachine.sendMessage(ClientModeStateMachine.CMD_START);
}

ClientModeStateMachine也是个状态机,该状态机只有两个状态,初始状态为IdleState。

private class IdleState extends State {……public boolean processMessage(Message message) {switch (message.what) {case CMD_START:mClientInterfaceName = mWifiNative.setupInterfaceForClientMode(false, mWifiNativeInterfaceCallback);……
}

可以看到,函数里面有这样mWifiNative.setupInterfaceForClientMode的调用,继续。

3.5、android/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java

public String setupInterfaceForClientMode(boolean lowPrioritySta,
@NonNull InterfaceCallback interfaceCallback) {synchronized (mLock) {if (!startHal()) {Log.e(TAG, "Failed to start Hal");mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal();return null;}if (!startSupplicant()) {Log.e(TAG, "Failed to start supplicant");mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();return null;}if (mWificondControl.setupInterfaceForClientMode(iface.name) == null) {Log.e(TAG, "Failed to setup iface in wificond on " + iface);teardownInterface(iface.name);mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToWificond();return null;}mWifiMonitor.startMonitoring(iface.name);……}
}

到这里就可以看到一些关键性的操作:

启动Hal:startHal();

启动supplicant:startSupplicant();

加载驱动(loadDriver):setupInterfaceForClientMode();

启动WifiMonitor:WifiMonitor.startMonitoring()。

WifiMonitor.startMonitoring():这一步主要是在WifiMonitor中建立与wpa_supplicant通信的socket通道、创建一个线程接收底层事件并分发处理。这里会创建两个socket通道与wpa_s通信,一个用于下发指令,另一个用于接收事件。成功后WifiMonitor会向WifiStateMachine发送一个代表socket通信建立成功的消息:SUP_CONNECTION_EVENT;收到这个消息就表示Wifi已经启动成功了。

到这里,我们选择startHal()这一条线看下去。

private boolean startHal() {synchronized (mLock) {if (!mIfaceMgr.hasAnyIface()) if (mWifiVendorHal.isVendorHalSupported()) {if (!mWifiVendorHal.startVendorHal()) {Log.e(TAG, "Failed to start vendor HAL");return false;}} else {Log.i(TAG, "Vendor Hal not supported, ignoring start.");}return true;}
}

看mWifiVendorHal.startVendorHal()。

3.6、android/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiVendorHal.java

public boolean startVendorHal() {synchronized (sLock) {if (!mHalDeviceManager.start()) {mLog.err("Failed to start vendor HAL").flush();return false;}mLog.info("Vendor Hal started successfully").flush();return true;}
}

看mHalDeviceaManager.start()。

3.7、android/frameworks/opt/net/wifi/service/java/com/android/server/wifi/HalDeviceManager.java

public boolean start() {return startWifi();
}

继续。

private boolean startWifi() {……synchronized (mLock) {try {WifiStatus status = mWifi.start();……}}
}

看WifiStatus status = mWifi.start(),先看下mWifi是什么。

//java通过import来导入他想引用的库,下面的库就是Vendor Hal Hidl编译生成的库。
import android.hardware.wifi.V1_0.IWifi;
……
public class HalDeviceManager {private IWifi mWifi;……
}
protected IWifi getWifiServiceMockable() {try {return IWifi.getService();} catch (RemoteException e) {Log.e(TAG, "Exception getting IWifi service: " + e);return null;}
}

可以看到,代码来到了IWifi.getService(),也就是获取android.hardware.wifi@1.0-service。

至此,就进入Hidl层了。

4、WiFi Hidl

4.1、android/hardware/interfaces/wifi/1.2/IWifi.hal

IWifi.hal这个文件,在编译之后,会在out/soong/.intermediates/hardware/interfaces/wifi/1.0/android.hardware.wifi_V1.0-java_gen_java/gen/android/hardware/wifi/V1_0/IWifi.java路径下生成一个名为IWifi.java的文件,我们来看到这个文件。

 public static IWifi getService(String serviceName) throws android.os.RemoteException {return IWifi.asInterface(android.os.HwBinder.getService("android.hardware.wifi@1.0::IWifi",serviceName));
}

再看一下 IWifi.java的asInterface方法。

IWifi.asInterface(android.os.HwBinder.getService("android.hardware.wifi@1.0::IWifi",serviceName));

从而我们就可以知道IWifi对应的服务端了。

4.2、android/hardware/interfaces/wifi/1.2/default/wifi.cpp

4.1里提供了获取service的方法,那么我们回到3.7,在那里调用了mWifi.start(),这个start其实就是IWifi对应的服务端里面的start方法。

Return<void> Wifi::start(start_cb hidl_status_cb) {return validateAndCall(this, WifiStatusCode::ERROR_UNKNOWN, &Wifi::startInternal, hidl_status_cb);
}
WifiStatus Wifi::startInternal() {……WifiStatus wifi_status = initializeModeControllerAndLegacyHal();……
}
WifiStatus Wifi::initializeModeControllerAndLegacyHal() {if (!mode_controller_->initialize()) {LOG(ERROR) << "Failed to initialize firmware mode controller";return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);}legacy_hal::wifi_error legacy_status = legacy_hal_->initialize();if (legacy_status != legacy_hal::WIFI_SUCCESS) {LOG(ERROR) << "Failed to initialize legacy HAL: "<< legacyErrorToString(legacy_status);return createWifiStatusFromLegacyError(legacy_status);}return createWifiStatus(WifiStatusCode::SUCCESS);
}

可以看到,代码中有这么一段调用mode_controller_->initialize,那么,这个mode_controller_是什么呢?

4.3、android/hardware/interfaces/wifi/1.2/default/wifi.h

std::shared_ptr<mode_controller::WifiModeController> mode_controller_;

mode_controller_在wifi.h中定义,其实mode_controller_就是wifi_mode_controller.cpp的一个对象。

4.4、android/hardware/interfaces/wifi/1.2/default/wifi_mode_controller.cpp

bool WifiModeController::initialize() {if (!driver_tool_->LoadDriver()) {LOG(ERROR) << "Failed to load WiFi driver";return false;}return true;
}

可以看到,这里有这么一个调用driver_tool_->LoadDriver。

4.4、android/hardware/interfaces/wifi/1.2/default/wifi_mode_controller.h

std::unique_ptr<wifi_hal::DriverTool> driver_tool_;

可以看到driver_tool_是由wifi_hal中的DriverTool实例而来,显然,现在就进入到了WiFi Hal层了。

5、WiFi Hal

5.1、android/frameworks/opt/net/wifi/libwifi_hal/driver_tool.cpp

bool DriverTool::LoadDriver() {return ::wifi_load_driver() == 0;
}

5.2、android/frameworks/opt/net/wifi/libwifi_hal/wifi_hal_common.cpp

int wifi_load_driver() {//如果WiFi驱动已经加载,则函数调用结束,直接返回0if (is_wifi_driver_loaded()) {return 0;}//加载WiFi驱动模块,失败返回-1if (insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG) < 0)return -1;……//如果成功加载驱动模块,则会设置安卓属性为okproperty_set(DRIVER_PROP_NAME, "ok");
}

到这里,打开WiFi的调用流程也就分析完了,因为只是打开WiFi,没有涉及到连接AP或者其他功能,所以就没有进入到WiFi Driver层了。

不过其他操作的流程也大体一致,有耐心的朋友可以看一看。

SDIO_WiFi驱动学习之安卓WLAN架构介绍及调用流程分析相关推荐

  1. SDIO_WiFi驱动学习之SDIO架构介绍及源码分析

    一.引言 因为WiFi驱动比较复杂,所以WiFi驱动的博客将多分几篇来写. 本篇博客主要介绍Linux下的SDIO架构及源码分析. 本文部分内容摘抄自网络,若有侵权,请联系删除. 二.SDIO WiF ...

  2. K8S架构设计及工作流程分析

    Kubernetes架构设计 核心组件 api server 功能 controller manager 负责维护集群的状态 scheduler 负责资源的调度按照预定的调度策略将Pod调度到相应的机 ...

  3. 无线通信网络学习之Wlan架构介绍

    前言: 一.无线局域网 (WLAN) 是什么? 无线局域网 (Wireless Local Area Network) 是以射频无线电波通信技术构建的局域网,虽不采用缆线,但也能提供传统有线局域网的所 ...

  4. Spring_AOP架构介绍与源码分析(含事务深度分析)

    请见链接:http://edu.51cto.com/course/16573.html?source=so 第1章课程介绍6分钟1节 1-1课程介绍[免费试看]06:11 第2章AOP深入分析52分钟 ...

  5. LNMP架构介绍及原理流程

    LAMP==Linux+Apache+Mysql+PHP LNMP==Linux+Nginx+Mysql+PHP 以上两个架构是目前网站的主流架构 LNMP的工作原理 LNMP是指LNMP==Linu ...

  6. MySQL 文件结构、逻辑架构及 sql 执行流程分析作者:Java后端架构

    1.MySQL 文件说明 1.1 MySQL 文件夹文件 linux 服务器上 MySQL 安装好之后都有如下文件: auto.cnf:每一个 MySQL 实例都有一个唯一 ID 蓝色文件夹:表示数据 ...

  7. mysql表文件与结构_MySQL文件结构、逻辑架构及sql执行流程分析

    1.MySQL文件说明 1.1 MySQL文件夹文件 linux服务器上MySQL安装好之后都有如下文件: auto.cnf:每一个MySQL实例都有一个唯一ID 蓝色文件夹:表示数据库,每个数据库对 ...

  8. linux 驱动probe 被调用流程分析

    前言: 对于linux platform device 和driver,一个driver可对应多个device,通过名字进行匹配,调用驱动里边实现的probe函数,本文以一个i2c设备为例,从驱动的i ...

  9. linux 驱动probe 被调用流程分析 理解1

    原文链接:https://blog.csdn.net/goodnight1994/article/details/82082089 修改个别错误呈现 前言: 对于linux platform devi ...

最新文章

  1. 探索未知种族之osg类生物---器官初始化四
  2. 数据库单表数据过亿_最受欢迎的三大数据库,你用过吗?
  3. iOS开发中视图相关的小笔记:push、modal、popover、replace、custom
  4. 【Linux】14_文件打包及压缩
  5. linux下source insight安装以及打开
  6. Linux 浏览网址汇集
  7. OC语言Block 续
  8. Windows远程桌面的使用(Remote Desktop Connection)
  9. 平板电脑硬件如何测试软件,先锋(Pioneer)G71平板电脑软件测试评测-ZOL中关村在线...
  10. 探索专有领域的端到端ASR解决之道
  11. AR 圈最大收购案 Ubimax 10 亿“卖身”背后:究竟什么才是真正值钱的东西?
  12. Android ANT多渠道打包
  13. 新任项目经理的五项必修课 (转)
  14. 计算机网络CDMA是什么,CDMA是什么
  15. 【转】LaTeX 符号命令大全
  16. 2019年安徽省c语言二级答案,2019年春江苏省计算机二级C语言真题
  17. 智商一般学计算机,IQ最高的十大专业公布 智商不高慎选
  18. Golang工具集-String工具,时间工具,http工具等
  19. apple watch更新系统时出现红色感叹号怎么办?
  20. 中年妇女xxx_2019年国际妇女节庆祝活动

热门文章

  1. mysql 存储视频_数据库中怎样存储视频?谢谢各位
  2. 大学生如何合理利用计算机,大学生如何安排自己的课余时间?6招,学霸教会你正确使用手机...
  3. 谷歌高级软件工程师的一天
  4. MRT工具谢幕,HEG华丽登场
  5. Unity技术手册 - 生命周期内速度限制(Limit Velocity Over Lifetime)子模块和速度继承(Inherit Velocity)子模块
  6. 如何下载浏览器中的flash 视频
  7. 为什么我的燃尽图没更新?
  8. python爬虫之糗事百科
  9. 引领盲返消费新潮流,共创社交电商新时代
  10. 【python爬虫】学习笔记1-爬取某网站妹子图片