记一次rk平台热点打开流程追踪记录

问题描述

rk平台偶现热点打开失败,错误日志如下:

I/android.hardware.wifi@1.0-service: Starting legacy HAL
E/android.hardware.wifi@1.0-service: Could not set interface flags for wlan0 (Operation not permitted)
E/android.hardware.wifi@1.0-service: Failed to set WiFi interface up
E/android.hardware.wifi@1.0-service: Failed to start legacy HAL: UNKNOWN
E/HalDevMgr: executeChipReconfiguration: configureChip error: 9 (unknown)
E/WifiVendorHal: Failed to create AP iface
E/WifiNative: Failed to create AP iface in vendor HAL
E/SoftApManager: setup failure when creating ap interface.
V/WifiManager: SoftApCallbackProxy: onStateChanged: state=14, failureReason=0
E/WifiController: SoftAP start failed

故借此机会准备从Java层开始学习热点的打开流程

开始

1.首先,我们要打开热点,调用是ConnectivityManager中的如下方法:

 public void startTethering(int type, boolean showProvisioningUi,final OnStartTetheringCallback callback, Handler handler) {Preconditions.checkNotNull(callback, "OnStartTetheringCallback cannot be null.");ResultReceiver wrappedCallback = new ResultReceiver(handler) {@Overrideprotected void onReceiveResult(int resultCode, Bundle resultData) {if (resultCode == TETHER_ERROR_NO_ERROR) {callback.onTetheringStarted();} else {callback.onTetheringFailed();}}};try {String pkgName = mContext.getOpPackageName();Log.i(TAG, "startTethering caller:" + pkgName);mService.startTethering(type, wrappedCallback, showProvisioningUi, pkgName);} catch (RemoteException e) {Log.e(TAG, "Exception trying to start tethering.", e);wrappedCallback.send(TETHER_ERROR_SERVICE_UNAVAIL, null);}}

此方法有四个参数:
type:热点类型,有三种TETHERING_WIFITETHERING_USBTETHERING_BLUETOOTH(一般我们用到的个人热点就是TETHERING_WIFI
showProvisioningUi:当设置为true是,将展示一个类似使用帮助的页面(需要厂商自己实现)
callback:热点打开结果的回调
handler:可以指定执行上面回调函数的线程

2.找到ConnectivityService的startTethering方法

 @Overridepublic void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi,String callerPkg) {ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);if (!isTetheringSupported()) {receiver.send(ConnectivityManager.TETHER_ERROR_UNSUPPORTED, null);return;}mTethering.startTethering(type, receiver, showProvisioningUi);}

显示权限检查,注释已经写的很明白了,之后判断是否支持热点,之后就调到了Tethering中的startTethering方法

 public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {if (!isTetherProvisioningRequired()) {//当没有定义热点UI时就会直接打开热点enableTetheringInternal(type, true, receiver);return;}if (showProvisioningUi) {runUiTetherProvisioningAndEnable(type, receiver);} else {runSilentTetherProvisioningAndEnable(type, receiver);}}

接下来看enableTetheringInternal方法

 /*** Enables or disables tethering for the given type. This should only be called once* provisioning has succeeded or is not necessary. It will also schedule provisioning rechecks* for the specified interface.*/private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) {boolean isProvisioningRequired = enable && isTetherProvisioningRequired();int result;switch (type) {case TETHERING_WIFI:result = setWifiTethering(enable);if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {scheduleProvisioningRechecks(type);}sendTetherResult(receiver, result);break;case TETHERING_USB:result = setUsbTethering(enable);if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {scheduleProvisioningRechecks(type);}sendTetherResult(receiver, result);break;case TETHERING_BLUETOOTH:setBluetoothTethering(enable, receiver);break;default:Log.w(TAG, "Invalid tether type.");sendTetherResult(receiver, TETHER_ERROR_UNKNOWN_IFACE);}}

主要看TETHERING_WIFI

 private int setWifiTethering(final boolean enable) {int rval = TETHER_ERROR_MASTER_ERROR;final long ident = Binder.clearCallingIdentity();try {synchronized (mPublicSync) {mWifiTetherRequested = enable;final WifiManager mgr = getWifiManager();if ((enable && mgr.startSoftAp(null /* use existing wifi config */)) ||(!enable && mgr.stopSoftAp())) {rval = TETHER_ERROR_NO_ERROR;}}} finally {Binder.restoreCallingIdentity(ident);}return rval;}

3.找到startSoftAp的实现,在WifiServiceImpl.java中

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

 /*** see {@link android.net.wifi.WifiManager#startSoftAp(WifiConfiguration)}* @param wifiConfig SSID, security and channel details as part of WifiConfiguration* @return {@code true} if softap start was triggered* @throws SecurityException if the caller does not have permission to start softap*/@Overridepublic boolean startSoftAp(WifiConfiguration wifiConfig) {// NETWORK_STACK is a signature only permission.enforceNetworkStackPermission();mLog.info("startSoftAp uid=%").c(Binder.getCallingUid()).flush();synchronized (mLocalOnlyHotspotRequests)    {// If a tethering request comes in while we have LOHS running (or requested), call stop// for softap mode and restart softap with the tethering config.if (!mLocalOnlyHotspotRequests.isEmpty()) {stopSoftApInternal();}return startSoftApInternal(wifiConfig, WifiManager.IFACE_IP_MODE_TETHERED);}}/*** Internal method to start softap mode. Callers of this method should have already checked* proper permissions beyond the NetworkStack permission.*/private boolean startSoftApInternal(WifiConfiguration wifiConfig, int mode) {mLog.trace("startSoftApInternal uid=% mode=%").c(Binder.getCallingUid()).c(mode).flush();// null wifiConfig is a meaningful input for CMD_SET_APif (wifiConfig == null || WifiApConfigStore.validateApWifiConfiguration(wifiConfig)) {SoftApModeConfiguration softApConfig = new SoftApModeConfiguration(mode, wifiConfig);mWifiController.sendMessage(CMD_SET_AP, 1, 0, softApConfig);return true;}Slog.e(TAG, "Invalid WifiConfiguration");return false;}

4.接着到WifiController.java这是个状态机,真正操作打开热点是在WifiStateMachinePrime.java中:

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

 /*** Method to enable soft ap for wifi hotspot.** The supplied SoftApModeConfiguration includes the target softap WifiConfiguration (or null if* the persisted config is to be used) and the target operating mode (ex,* {@link WifiManager.IFACE_IP_MODE_TETHERED} {@link WifiManager.IFACE_IP_MODE_LOCAL_ONLY}).** @param wifiConfig SoftApModeConfiguration for the hostapd softap*/public void enterSoftAPMode(@NonNull SoftApModeConfiguration wifiConfig) {mHandler.post(() -> {startSoftAp(wifiConfig);});}
 private void startSoftAp(SoftApModeConfiguration softapConfig) {Log.d(TAG, "Starting SoftApModeManager");WifiConfiguration config = softapConfig.getWifiConfiguration();if (config != null && config.SSID != null) {Log.d(TAG, "Passing config to SoftApManager! " + config);} else {config = null;}SoftApCallbackImpl callback = new SoftApCallbackImpl();ActiveModeManager manager = mWifiInjector.makeSoftApManager(callback, softapConfig);callback.setActiveModeManager(manager);manager.start();mActiveModeManagers.add(manager);updateBatteryStatsWifiState(true);}

5.找到SoftApManager.java的start()方法:

frameworks/opt/net/wifi/service/java/com/android/server/wifi/SoftApManager.java

 /*** Start soft AP with the supplied config.*/public void start() {mStateMachine.sendMessage(SoftApStateMachine.CMD_START, mApConfig);}

又一个状态机

 private class IdleState extends State {@Overridepublic void enter() {mApInterfaceName = null;mIfaceIsUp = false;}@Overridepublic boolean processMessage(Message message) {switch (message.what) {case CMD_START:Log.e(TAG, "CMD_START.");mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode(mWifiNativeInterfaceCallback);if (TextUtils.isEmpty(mApInterfaceName)) {Log.e(TAG, "setup failure when creating ap interface.");updateApState(WifiManager.WIFI_AP_STATE_FAILED,WifiManager.WIFI_AP_STATE_DISABLED,WifiManager.SAP_START_FAILURE_GENERAL);mWifiMetrics.incrementSoftApStartResult(false, WifiManager.SAP_START_FAILURE_GENERAL);break;}updateApState(WifiManager.WIFI_AP_STATE_ENABLING,WifiManager.WIFI_AP_STATE_DISABLED, 0);int result = startSoftAp((WifiConfiguration) message.obj);if (result != SUCCESS) {int failureReason = WifiManager.SAP_START_FAILURE_GENERAL;if (result == ERROR_NO_CHANNEL) {failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;}updateApState(WifiManager.WIFI_AP_STATE_FAILED,WifiManager.WIFI_AP_STATE_ENABLING,failureReason);stopSoftAp();mWifiMetrics.incrementSoftApStartResult(false, failureReason);break;}transitionTo(mStartedState);break;default:// Ignore all other commands.break;}return HANDLED;}}

6.setupInterfaceForSoftApMode方法,此处就比较重要了,需要逐条分析:

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

 /*** Setup an interface for Soft AP mode operations.** This method configures an interface in AP mode in all the native daemons* (wificond, wpa_supplicant & vendor HAL).** @param interfaceCallback Associated callback for notifying status changes for the iface.* @return Returns the name of the allocated interface, will be null on failure.*/public String setupInterfaceForSoftApMode(@NonNull InterfaceCallback interfaceCallback) {Log.e(TAG,"setupInterfaceForSoftApMode");synchronized (mLock) {if (!startHal()) {Log.e(TAG, "Failed to start Hal");mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHal();return null;}Log.e(TAG, "liyang Hal start success");Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_AP);if (iface == null) {Log.e(TAG, "Failed to allocate new AP iface");return null;}iface.externalListener = interfaceCallback;iface.name = createApIface(iface);if (TextUtils.isEmpty(iface.name)) {Log.e(TAG, "Failed to create AP iface in vendor HAL");mIfaceMgr.removeIface(iface.id);mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHal();return null;}if (mWificondControl.setupInterfaceForSoftApMode(iface.name) == null) {Log.e(TAG, "Failed to setup iface in wificond on " + iface);teardownInterface(iface.name);mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToWificond();return null;}iface.networkObserver = new NetworkObserverInternal(iface.id);if (!registerNetworkObserver(iface.networkObserver)) {Log.e(TAG, "Failed to register network observer on " + iface);teardownInterface(iface.name);return null;}// Just to avoid any race conditions with interface state change callbacks,// update the interface state before we exit.onInterfaceStateChanged(iface, isInterfaceUp(iface.name));Log.i(TAG, "Successfully setup " + iface);return iface.name;}}

7先看startHal()方法:

 /** Helper method invoked to start supplicant if there were no ifaces */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;}}

首次进来,还未调用 allocateIface 方法,所以 mIfaceMgr.hasAnyIface()false
同时mWifiVendorHal.isVendorHalSupported()也必然为true,接着就到了WifiVendorHal.java中:
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiVendorHal.java

 /*** Bring up the HIDL Vendor HAL and configure for AP (Access Point) mode** @return true for success*/public boolean startVendorHalAp() {synchronized (sLock) {if (!startVendorHal()) {return false;}if (TextUtils.isEmpty(createApIface(null))) {stopVendorHal();return false;}return true;}}
     /*** Bring up the HIDL Vendor HAL.* @return true on success, false otherwise.*/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;}}

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

 /*** Attempts to start Wi-Fi (using HIDL). Returns the success (true) or failure (false) or* the start operation. Will also dispatch any registered ManagerStatusCallback.onStart() on* success.** Note: direct call to HIDL.*/public boolean start() {return startWifi();}
    private boolean startWifi() {if (VDBG) Log.d(TAG, "startWifi");synchronized (mLock) {try {if (mWifi == null) {Log.w(TAG, "startWifi called but mWifi is null!?");return false;} else {int triedCount = 0;while (triedCount <= START_HAL_RETRY_TIMES) {WifiStatus status = mWifi.start();if (status.code == WifiStatusCode.SUCCESS) {initIWifiChipDebugListeners();managerStatusListenerDispatch();if (triedCount != 0) {Log.d(TAG, "start IWifi succeeded after trying "+ triedCount + " times");}return true;} else if (status.code == WifiStatusCode.ERROR_NOT_AVAILABLE) {// Should retry. Hal might still be stopping.Log.e(TAG, "Cannot start IWifi: " + statusString(status)+ ", Retrying...");try {Thread.sleep(START_HAL_RETRY_INTERVAL_MS);} catch (InterruptedException ignore) {// no-op}triedCount++;} else {// Should not retry on other failures.Log.e(TAG, "Cannot start IWifi: " + statusString(status));return false;}}Log.e(TAG, "Cannot start IWifi after trying " + triedCount + " times");return false;}} catch (RemoteException e) {Log.e(TAG, "startWifi exception: " + e);return false;}}}

mWifi.start() 开始就到了native层
hardware/interfaces/wifi/1.2/default/wifi.cpp

Return<void> Wifi::start(start_cb hidl_status_cb) {return validateAndCall(this, WifiStatusCode::ERROR_UNKNOWN,&Wifi::startInternal, hidl_status_cb);
}
WifiStatus Wifi::startInternal() {if (run_state_ == RunState::STARTED) {return createWifiStatus(WifiStatusCode::SUCCESS);} else if (run_state_ == RunState::STOPPING) {return createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE,"HAL is stopping");}WifiStatus wifi_status = initializeModeControllerAndLegacyHal();if (wifi_status.code == WifiStatusCode::SUCCESS) {// Create the chip instance once the HAL is started.chip_ = new WifiChip(kChipId, legacy_hal_, mode_controller_,feature_flags_);run_state_ = RunState::STARTED;for (const auto& callback : event_cb_handler_.getCallbacks()) {if (!callback->onStart().isOk()) {LOG(ERROR) << "Failed to invoke onStart callback";};}LOG(INFO) << "Wifi HAL started";} else {for (const auto& callback : event_cb_handler_.getCallbacks()) {if (!callback->onFailure(wifi_status).isOk()) {LOG(ERROR) << "Failed to invoke onFailure callback";}}LOG(ERROR) << "Wifi HAL start failed";}return wifi_status;
}
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);
}

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;
}

framework/opt/net/wifi/lbwifi_hal/driver_tool.cpp

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

frameworks/opt/net/wifi/libwifi_hal/wifi_hal_common.cpp

int wifi_load_driver() {#ifdef WIFI_DRIVER_MODULE_PATHchar* wifi_ko_path = NULL ;char* wifi_ko_arg =NULL;int i = 0;int count = 100;if (is_wifi_driver_loaded()) {return 0;}if (wifi_type[0] == 0) {check_wifi_chip_type_string(wifi_type);save_wifi_chip_type(wifi_type);}for (i=0; i< (int)(sizeof(module_list) / sizeof(module_list[0])); i++) {if (!strcmp(wifi_type , module_list[i].wifi_name)) {wifi_ko_path = module_list[i].wifi_module_path;wifi_ko_arg = module_list[i].wifi_module_arg;PLOG(ERROR) << "matched ko file path " << wifi_ko_path;break;}}if (wifi_ko_path == NULL) {PLOG(ERROR) << "falied to find wifi driver for type=" << wifi_type;return -1;}if (strstr(wifi_ko_path, MVL_DRIVER_MODULE_NAME)) {insmod(MLAN_DRIVER_MODULE_PATH, "");}if (insmod(wifi_ko_path, wifi_ko_arg) < 0) {return -1;}
#endif#ifdef WIFI_DRIVER_STATE_CTRL_PARAMif (is_wifi_driver_loaded()) {return 0;}if (wifi_change_driver_state(WIFI_DRIVER_STATE_ON) < 0) return -1;
#endifwhile (count-- > 0) {if (is_wifi_driver_loaded()) {property_set(DRIVER_PROP_NAME, "ok");return 0;}usleep(200000);}property_set(DRIVER_PROP_NAME, "timeout");return -1;
}

这里面就开始加载wlan0模块,并通过检测/proc/net/dev节点中是否存在wlan0判断是否加载完成,具体模块的初始化在:
kernel/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.c

dhd_module_init(void)
{int err;int retry = 0;printf("%s: in %s\n", __FUNCTION__, dhd_version);DHD_PERIM_RADIO_INIT();if (firmware_path[0] != '\0') {strncpy(fw_bak_path, firmware_path, MOD_PARAM_PATHLEN);fw_bak_path[MOD_PARAM_PATHLEN-1] = '\0';}if (nvram_path[0] != '\0') {strncpy(nv_bak_path, nvram_path, MOD_PARAM_PATHLEN);nv_bak_path[MOD_PARAM_PATHLEN-1] = '\0';}do {err = dhd_wifi_platform_register_drv();if (!err) {register_reboot_notifier(&dhd_reboot_notifier);break;} else {DHD_ERROR(("%s: Failed to load the driver, try cnt %d\n",__FUNCTION__, retry));strncpy(firmware_path, fw_bak_path, MOD_PARAM_PATHLEN);firmware_path[MOD_PARAM_PATHLEN-1] = '\0';strncpy(nvram_path, nv_bak_path, MOD_PARAM_PATHLEN);nvram_path[MOD_PARAM_PATHLEN-1] = '\0';}} while (retry--);dhd_create_to_notifier_skt();if (err) {DHD_ERROR(("%s: Failed to load driver max retry reached**\n", __FUNCTION__));} else {if (!dhd_download_fw_on_driverload) {dhd_driver_init_done = TRUE;}}printf("%s: Exit err=%d\n", __FUNCTION__, err);return err;
}

当wlan0模块加载完成后,接着看legacy_hal_->initialize(),初始化hal函数:
hardware/interfaces/wifi/1.2/default/wifi_legacy_hal.cpp

wifi_error WifiLegacyHal::initialize() {LOG(ERROR) << "Initialize legacy HAL";// TODO: Add back the HAL Tool if we need to. All we need from the HAL tool// for now is this function call which we can directly call.if (!initHalFuncTableWithStubs(&global_func_table_)) {LOG(ERROR)<< "Failed to initialize legacy hal function table with stubs";return WIFI_ERROR_UNKNOWN;}wifi_error status = init_wifi_vendor_hal_func_table(&global_func_table_);if (status != WIFI_SUCCESS) {LOG(ERROR) << "Failed to initialize legacy hal function table";}return status;
}

到此,初始化工作就完成了,下面回头看第 6 点中的 createApIface(iface) 方法

8.创建AP Iface

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

 /*** Helper function to handle creation of AP iface.* For devices which do not the support the HAL, this will bypass HalDeviceManager &* teardown any existing iface.*/private String createApIface(@NonNull Iface iface) {synchronized (mLock) {if (mWifiVendorHal.isVendorHalSupported()) {return mWifiVendorHal.createApIface(new InterfaceDestoyedListenerInternal(iface.id));} else {Log.i(TAG, "Vendor Hal not supported, ignoring createApIface.");return handleIfaceCreationWhenVendorHalNotSupported(iface);}}}

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

     /*** Create a AP iface using {@link HalDeviceManager}.** @param destroyedListener Listener to be invoked when the interface is destroyed.* @return iface name on success, null otherwise.*/public String createApIface(InterfaceDestroyedListener destroyedListener) {synchronized (sLock) {IWifiApIface iface = mHalDeviceManager.createApIface(new ApInterfaceDestroyedListenerInternal(destroyedListener), null);if (iface == null) {mLog.err("Failed to create AP iface").flush();return stringResult(null);}String ifaceName = mHalDeviceManager.getName((IWifiIface) iface);if (TextUtils.isEmpty(ifaceName)) {mLog.err("Failed to get iface name").flush();return stringResult(null);}if (!retrieveWifiChip((IWifiIface) iface)) {mLog.err("Failed to get wifi chip").flush();return stringResult(null);}mIWifiApIfaces.put(ifaceName, iface);return ifaceName;}}

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

 /*** Create AP interface if possible (see createStaIface doc).*/public IWifiApIface createApIface(@Nullable InterfaceDestroyedListener destroyedListener,@Nullable Handler handler) {return (IWifiApIface) createIface(IfaceType.AP, false, destroyedListener, handler);}
 private IWifiIface createIface(int ifaceType, boolean lowPriority,InterfaceDestroyedListener destroyedListener, Handler handler) {if (mDbg) {Log.d(TAG, "createIface: ifaceType=" + ifaceType + ", lowPriority=" + lowPriority);}synchronized (mLock) {WifiChipInfo[] chipInfos = getAllChipInfo();if (chipInfos == null) {Log.e(TAG, "createIface: no chip info found");stopWifi(); // major error: shutting downreturn null;}if (!validateInterfaceCache(chipInfos)) {Log.e(TAG, "createIface: local cache is invalid!");stopWifi(); // major error: shutting downreturn null;}IWifiIface iface = createIfaceIfPossible(chipInfos, ifaceType, lowPriority,destroyedListener, handler);if (iface != null) { // means that some configuration has changedif (!dispatchAvailableForRequestListeners()) {return null; // catastrophic failure - shut down}}return iface;}}
 /*** Performs chip reconfiguration per the input:* - Removes the specified interfaces* - Reconfigures the chip to the new chip mode (if necessary)* - Creates the new interface** Returns the newly created interface or a null on any error.*/private IWifiIface executeChipReconfiguration(IfaceCreationData ifaceCreationData,int ifaceType) {if (mDbg) {Log.d(TAG, "executeChipReconfiguration: ifaceCreationData=" + ifaceCreationData+ ", ifaceType=" + ifaceType);}synchronized (mLock) {try {// is this a mode change?boolean isModeConfigNeeded = !ifaceCreationData.chipInfo.currentModeIdValid|| ifaceCreationData.chipInfo.currentModeId != ifaceCreationData.chipModeId;if (mDbg) Log.d(TAG, "isModeConfigNeeded=" + isModeConfigNeeded);// first delete interfaces/change modesif (isModeConfigNeeded) {// remove all interfaces pre mode-change// TODO: is this necessary? note that even if we don't want to explicitly// remove the interfaces we do need to call the onDeleted callbacks - which// this doesfor (WifiIfaceInfo[] ifaceInfos: ifaceCreationData.chipInfo.ifaces) {for (WifiIfaceInfo ifaceInfo: ifaceInfos) {removeIfaceInternal(ifaceInfo.iface); // ignore return value}}WifiStatus status = ifaceCreationData.chipInfo.chip.configureChip(ifaceCreationData.chipModeId);if (status.code != WifiStatusCode.SUCCESS) {Log.e(TAG, "executeChipReconfiguration: configureChip error: "+ statusString(status));return null;}} else {// remove all interfaces on the delete listfor (WifiIfaceInfo ifaceInfo: ifaceCreationData.interfacesToBeRemovedFirst) {removeIfaceInternal(ifaceInfo.iface); // ignore return value}}// create new interfaceMutable<WifiStatus> statusResp = new Mutable<>();Mutable<IWifiIface> ifaceResp = new Mutable<>();switch (ifaceType) {case IfaceType.STA:ifaceCreationData.chipInfo.chip.createStaIface((WifiStatus status, IWifiStaIface iface) -> {statusResp.value = status;ifaceResp.value = iface;});break;case IfaceType.AP:ifaceCreationData.chipInfo.chip.createApIface((WifiStatus status, IWifiApIface iface) -> {statusResp.value = status;ifaceResp.value = iface;});break;case IfaceType.P2P:ifaceCreationData.chipInfo.chip.createP2pIface((WifiStatus status, IWifiP2pIface iface) -> {statusResp.value = status;ifaceResp.value = iface;});break;case IfaceType.NAN:ifaceCreationData.chipInfo.chip.createNanIface((WifiStatus status, IWifiNanIface iface) -> {statusResp.value = status;ifaceResp.value = iface;});break;}if (statusResp.value.code != WifiStatusCode.SUCCESS) {Log.e(TAG, "executeChipReconfiguration: failed to create interface ifaceType="+ ifaceType + ": " + statusString(statusResp.value));return null;}return ifaceResp.value;} catch (RemoteException e) {Log.e(TAG, "executeChipReconfiguration exception: " + e);return null;}}}

主要看里面的

 WifiStatus status = ifaceCreationData.chipInfo.chip.configureChip(ifaceCreationData.chipModeId);

这里就又走到了native
hardware/interfaces/wifi/1.2/default/wifi_chip.cpp

Return<void> WifiChip::configureChip(ChipModeId mode_id,configureChip_cb hidl_status_cb) {return validateAndCallWithLock(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,&WifiChip::configureChipInternal, hidl_status_cb, mode_id);
}
WifiStatus WifiChip::configureChipInternal(/* NONNULL */ std::unique_lock<std::recursive_mutex>* lock,ChipModeId mode_id) {LOG(INFO) << "configureChipInternal mode: " << mode_id << ", current mode: " << current_mode_id_;if (!isValidModeId(mode_id)) {return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);}if (mode_id == current_mode_id_) {LOG(DEBUG) << "Already in the specified mode " << mode_id;return createWifiStatus(WifiStatusCode::SUCCESS);}WifiStatus status = handleChipConfiguration(lock, mode_id);if (status.code != WifiStatusCode::SUCCESS) {for (const auto& callback : event_cb_handler_.getCallbacks()) {if (!callback->onChipReconfigureFailure(status).isOk()) {LOG(ERROR)<< "Failed to invoke onChipReconfigureFailure callback";}}return status;}for (const auto& callback : event_cb_handler_.getCallbacks()) {if (!callback->onChipReconfigured(mode_id).isOk()) {LOG(ERROR) << "Failed to invoke onChipReconfigured callback";}}current_mode_id_ = mode_id;LOG(INFO) << "Configured chip in mode " << mode_id;return status;
}
WifiStatus WifiChip::handleChipConfiguration(/* NONNULL */ std::unique_lock<std::recursive_mutex>* lock,ChipModeId mode_id) {LOG(INFO) << "handleChipConfiguration ";// If the chip is already configured in a different mode, stop// the legacy HAL and then start it after firmware mode change.if (isValidModeId(current_mode_id_)) {LOG(INFO) << "Reconfiguring chip from mode " << current_mode_id_<< " to mode " << mode_id;invalidateAndRemoveAllIfaces();legacy_hal::wifi_error legacy_status =legacy_hal_.lock()->stop(lock, []() {});if (legacy_status != legacy_hal::WIFI_SUCCESS) {LOG(ERROR) << "Failed to stop legacy HAL: "<< legacyErrorToString(legacy_status);return createWifiStatusFromLegacyError(legacy_status);}}// Firmware mode change not needed for V2 devices.bool success = true;if (mode_id == kV1StaChipModeId) {success = mode_controller_.lock()->changeFirmwareMode(IfaceType::STA);} else if (mode_id == kV1ApChipModeId) {success = mode_controller_.lock()->changeFirmwareMode(IfaceType::AP);}if (!success) {return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);}legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->start();if (legacy_status != legacy_hal::WIFI_SUCCESS) {LOG(ERROR) << "Failed to start legacy HAL: "<< legacyErrorToString(legacy_status);return createWifiStatusFromLegacyError(legacy_status);}// Every time the HAL is restarted, we need to register the// radio mode change callback.WifiStatus status = registerRadioModeChangeCallback();if (status.code != WifiStatusCode::SUCCESS) {// This probably is not a critical failure?LOG(ERROR) << "Failed to register radio mode change callback";}return createWifiStatus(WifiStatusCode::SUCCESS);
}

hardware/interfaces/wifi/1.2/default/wifi_legacy_hal.cpp

wifi_error WifiLegacyHal::start() {// Ensure that we're starting in a good state.//CHECK(global_func_table_.wifi_initialize && !global_handle_ &&//      iface_name_to_handle_.empty() && !awaiting_event_loop_termination_);if (is_started_) {LOG(INFO) << "Legacy HAL already started";return WIFI_SUCCESS;}LOG(INFO) << "Waiting for the driver ready";wifi_error status = global_func_table_.wifi_wait_for_driver_ready();if (status == WIFI_ERROR_TIMED_OUT) {LOG(ERROR) << "Timed out awaiting driver ready";return status;}LOG(INFO) << "Starting legacy HAL";if (!iface_tool_.SetWifiUpState(true)) {LOG(ERROR) << "Failed to set WiFi interface up";return WIFI_ERROR_UNKNOWN;}status = global_func_table_.wifi_initialize(&global_handle_);if (status != WIFI_SUCCESS || !global_handle_) {LOG(ERROR) << "Failed to retrieve global handle";return status;}std::thread(&WifiLegacyHal::runEventLoop, this).detach();status = retrieveIfaceHandles();if (status != WIFI_SUCCESS || iface_name_to_handle_.empty()) {LOG(ERROR) << "Failed to retrieve wlan interface handle";return status;}LOG(INFO) << "Legacy HAL start complete";is_started_ = true;return WIFI_SUCCESS;
}

frameworks/opt/net/wifi/libwifi_system_iface/interface_tool.cpp

bool InterfaceTool::SetWifiUpState(bool request_up) {return SetUpState(kWlan0InterfaceName, request_up);
}
bool InterfaceTool::SetUpState(const char* if_name, bool request_up) {base::unique_fd sock(socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));if (sock.get() < 0) {LOG(ERROR) << "Failed to open socket to set up/down state ("<< strerror(errno) << ")";return false;}struct ifreq ifr;if (!GetIfState(if_name, sock.get(), &ifr)) {return false;  // logging done internally}const bool currently_up = ifr.ifr_flags & IFF_UP;if (currently_up == request_up) {return true;}if (request_up) {ifr.ifr_flags |= IFF_UP;} else {ifr.ifr_flags &= ~IFF_UP;}if (TEMP_FAILURE_RETRY(ioctl(sock.get(), SIOCSIFFLAGS, &ifr)) != 0) {LOG(ERROR) << "Could not set interface flags for " << if_name<< " (" << strerror(errno) << ")";return false;}return true;
}

这里通过ioctl调到了kernel中,具体过程不看了,直接找到实现的位置
kernel/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.c

static int
dhd_open(struct net_device *net)
{dhd_info_t *dhd = DHD_DEV_INFO(net);
#ifdef TOEuint32 toe_ol;
#endif
#ifdef BCM_FD_AGGRchar iovbuf[WLC_IOCTL_SMLEN];dbus_config_t config;uint32 agglimit = 0;uint32 rpc_agg = BCM_RPC_TP_DNGL_AGG_DPC; /* host aggr not enabled yet */
#endif /* BCM_FD_AGGR */int ifidx;int32 ret = 0;
#if defined(OOB_INTR_ONLY)uint32 bus_type = -1;uint32 bus_num = -1;uint32 slot_num = -1;wifi_adapter_info_t *adapter = NULL;
#endif
#if defined(WL_EXT_IAPSTA) && defined(ISAM_PREINIT)int bytes_written = 0;struct dhd_conf *conf;
#endifif (!dhd_download_fw_on_driverload) {if (!dhd_driver_init_done) {DHD_ERROR(("%s: WLAN driver is not initialized\n", __FUNCTION__));return -1;}}printf("%s: Enter %p\n", __FUNCTION__, net);DHD_MUTEX_LOCK();/* Init wakelock */if (!dhd_download_fw_on_driverload) {if (!(dhd->dhd_state & DHD_ATTACH_STATE_WAKELOCKS_INIT)) {DHD_OS_WAKE_LOCK_INIT(dhd);dhd->dhd_state |= DHD_ATTACH_STATE_WAKELOCKS_INIT;}
#ifdef SHOW_LOGTRACEskb_queue_head_init(&dhd->evt_trace_queue);if (!(dhd->dhd_state & DHD_ATTACH_LOGTRACE_INIT)) {ret = dhd_init_logstrs_array(dhd->pub.osh, &dhd->event_data);if (ret == BCME_OK) {dhd_init_static_strs_array(dhd->pub.osh, &dhd->event_data,st_str_file_path, map_file_path);dhd_init_static_strs_array(dhd->pub.osh, &dhd->event_data,rom_st_str_file_path, rom_map_file_path);dhd->dhd_state |= DHD_ATTACH_LOGTRACE_INIT;}}
#endif /* SHOW_LOGTRACE */}#if defined(PREVENT_REOPEN_DURING_HANG)/* WAR : to prevent calling dhd_open abnormally in quick succession after hang event */if (dhd->pub.hang_was_sent == 1) {DHD_ERROR(("%s: HANG was sent up earlier\n", __FUNCTION__));/* Force to bring down WLAN interface in case dhd_stop() is not called* from the upper layer when HANG event is triggered.*/if (!dhd_download_fw_on_driverload && dhd->pub.up == 1) {DHD_ERROR(("%s: WLAN interface is not brought down\n", __FUNCTION__));dhd_stop(net);} else {return -1;}}
#endif /* PREVENT_REOPEN_DURING_HANG */DHD_OS_WAKE_LOCK(&dhd->pub);DHD_PERIM_LOCK(&dhd->pub);dhd->pub.dongle_trap_occured = 0;dhd->pub.hang_was_sent = 0;dhd->pub.hang_reason = 0;dhd->pub.iovar_timeout_occured = 0;
#ifdef PCIE_FULL_DONGLEdhd->pub.d3ack_timeout_occured = 0;
#endif /* PCIE_FULL_DONGLE */#ifdef DHD_LOSSLESS_ROAMINGdhd->pub.dequeue_prec_map = ALLPRIO;
#endif
#if 0/** Force start if ifconfig_up gets called before START command*  We keep WEXT's wl_control_wl_start to provide backward compatibility*  This should be removed in the future*/ret = wl_control_wl_start(net);if (ret != 0) {DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret));ret = -1;goto exit;}
#endififidx = dhd_net2idx(dhd, net);DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx));if (ifidx < 0) {DHD_ERROR(("%s: Error: called with invalid IF\n", __FUNCTION__));ret = -1;goto exit;}if (!dhd->iflist[ifidx]) {DHD_ERROR(("%s: Error: called when IF already deleted\n", __FUNCTION__));ret = -1;goto exit;}if (ifidx == 0) {atomic_set(&dhd->pend_8021x_cnt, 0);if (!dhd_download_fw_on_driverload) {DHD_ERROR(("\n%s\n", dhd_version));
#ifdef WL_EXT_IAPSTAwl_ext_iapsta_attach_netdev(net, ifidx, dhd->iflist[ifidx]->bssidx);
#endif
#if defined(USE_INITIAL_SHORT_DWELL_TIME)g_first_broadcast_scan = TRUE;
#endif
#if defined(BT_OVER_SDIO)ret = dhd_bus_get(&dhd->pub, WLAN_MODULE);wl_android_set_wifi_on_flag(TRUE);
#elseret = wl_android_wifi_on(net);
#endif /* BT_OVER_SDIO */if (ret != 0) {DHD_ERROR(("%s : wl_android_wifi_on failed (%d)\n",__FUNCTION__, ret));ret = -1;goto exit;}
#if defined(WL_EXT_IAPSTA) && defined(ISAM_PREINIT)conf = dhd_get_conf(net);if (conf) {wl_android_ext_priv_cmd(net, conf->isam_init, 0, &bytes_written);wl_android_ext_priv_cmd(net, conf->isam_config, 0, &bytes_written);wl_android_ext_priv_cmd(net, conf->isam_enable, 0, &bytes_written);}
#endif}
#ifdef FIX_CPU_MIN_CLOCKif (dhd_get_fw_mode(dhd) == DHD_FLAG_HOSTAP_MODE) {dhd_init_cpufreq_fix(dhd);dhd_fix_cpu_freq(dhd);}
#endif /* FIX_CPU_MIN_CLOCK */
#if defined(OOB_INTR_ONLY)if (dhd->pub.conf->dpc_cpucore >= 0) {dhd_bus_get_ids(dhd->pub.bus, &bus_type, &bus_num, &slot_num);adapter = dhd_wifi_platform_get_adapter(bus_type, bus_num, slot_num);if (adapter) {printf("%s: set irq affinity hit %d\n", __FUNCTION__, dhd->pub.conf->dpc_cpucore);irq_set_affinity_hint(adapter->irq_num, cpumask_of(dhd->pub.conf->dpc_cpucore));}}
#endifif (dhd->pub.busstate != DHD_BUS_DATA) {#ifdef BCMDBUSdhd_set_path(&dhd->pub);DHD_MUTEX_UNLOCK();wait_event_interruptible_timeout(dhd->adapter->status_event,wifi_get_adapter_status(dhd->adapter, WIFI_STATUS_FW_READY),msecs_to_jiffies(DHD_FW_READY_TIMEOUT));DHD_MUTEX_LOCK();if ((ret = dbus_up(dhd->pub.bus)) != 0) {DHD_ERROR(("%s: failed to dbus_up with code %d\n", __FUNCTION__, ret));goto exit;} else {dhd->pub.busstate = DHD_BUS_DATA;}if ((ret = dhd_sync_with_dongle(&dhd->pub)) < 0) {DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret));goto exit;}
#else/* try to bring up bus */DHD_PERIM_UNLOCK(&dhd->pub);ret = dhd_bus_start(&dhd->pub);DHD_PERIM_LOCK(&dhd->pub);if (ret) {DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret));ret = -1;goto exit;}
#endif /* !BCMDBUS */}
#ifdef WL_EXT_IAPSTAwl_ext_iapsta_attach_name(net, ifidx);
#endifif (dhd_download_fw_on_driverload) {if (dhd->pub.conf->deepsleep)dhd_deepsleep(dhd, 0);}#ifdef BCM_FD_AGGRconfig.config_id = DBUS_CONFIG_ID_AGGR_LIMIT;memset(iovbuf, 0, sizeof(iovbuf));bcm_mkiovar("rpc_dngl_agglimit", (char *)&agglimit, 4,iovbuf, sizeof(iovbuf));if (!dhd_wl_ioctl_cmd(&dhd->pub, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0)) {agglimit = *(uint32 *)iovbuf;config.aggr_param.maxrxsf = agglimit >> BCM_RPC_TP_AGG_SF_SHIFT;config.aggr_param.maxrxsize = agglimit & BCM_RPC_TP_AGG_BYTES_MASK;DHD_ERROR(("rpc_dngl_agglimit %x : sf_limit %d bytes_limit %d\n",agglimit, config.aggr_param.maxrxsf, config.aggr_param.maxrxsize));if (bcm_rpc_tp_set_config(dhd->pub.info->rpc_th, &config)) {DHD_ERROR(("set tx/rx queue size and buffersize failed\n"));}} else {DHD_ERROR(("get rpc_dngl_agglimit failed\n"));rpc_agg &= ~BCM_RPC_TP_DNGL_AGG_DPC;}/* Set aggregation for TX */bcm_rpc_tp_agg_set(dhd->pub.info->rpc_th, BCM_RPC_TP_HOST_AGG_MASK,rpc_agg & BCM_RPC_TP_HOST_AGG_MASK);/* Set aggregation for RX */memset(iovbuf, 0, sizeof(iovbuf));bcm_mkiovar("rpc_agg", (char *)&rpc_agg, sizeof(rpc_agg), iovbuf, sizeof(iovbuf));if (!dhd_wl_ioctl_cmd(&dhd->pub, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) {dhd->pub.info->fdaggr = 0;if (rpc_agg & BCM_RPC_TP_HOST_AGG_MASK)dhd->pub.info->fdaggr |= BCM_FDAGGR_H2D_ENABLED;if (rpc_agg & BCM_RPC_TP_DNGL_AGG_MASK)dhd->pub.info->fdaggr |= BCM_FDAGGR_D2H_ENABLED;} else {DHD_ERROR(("%s(): Setting RX aggregation failed %d\n", __FUNCTION__, ret));}
#endif /* BCM_FD_AGGR */#ifdef BT_OVER_SDIOif (dhd->pub.is_bt_recovery_required) {DHD_ERROR(("%s: Send Hang Notification 2 to BT\n", __FUNCTION__));bcmsdh_btsdio_process_dhd_hang_notification(TRUE);}dhd->pub.is_bt_recovery_required = FALSE;
#endif/* dhd_sync_with_dongle has been called in dhd_bus_start or wl_android_wifi_on */memcpy(net->dev_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN);#ifdef TOE/* Get current TOE mode from dongle */if (dhd_toe_get(dhd, ifidx, &toe_ol) >= 0 && (toe_ol & TOE_TX_CSUM_OL) != 0) {dhd->iflist[ifidx]->net->features |= NETIF_F_IP_CSUM;} else {dhd->iflist[ifidx]->net->features &= ~NETIF_F_IP_CSUM;}
#endif /* TOE */#if defined(DHD_LB_RXP)__skb_queue_head_init(&dhd->rx_pend_queue);if (dhd->rx_napi_netdev == NULL) {dhd->rx_napi_netdev = dhd->iflist[ifidx]->net;memset(&dhd->rx_napi_struct, 0, sizeof(struct napi_struct));netif_napi_add(dhd->rx_napi_netdev, &dhd->rx_napi_struct,dhd_napi_poll, dhd_napi_weight);DHD_INFO(("%s napi<%p> enabled ifp->net<%p,%s>\n",__FUNCTION__, &dhd->rx_napi_struct, net, net->name));napi_enable(&dhd->rx_napi_struct);DHD_INFO(("%s load balance init rx_napi_struct\n", __FUNCTION__));skb_queue_head_init(&dhd->rx_napi_queue);} /* rx_napi_netdev == NULL */
#endif /* DHD_LB_RXP */#if defined(DHD_LB_TXP)/* Use the variant that uses locks */skb_queue_head_init(&dhd->tx_pend_queue);
#endif /* DHD_LB_TXP */#if defined(WL_CFG80211)if (unlikely(wl_cfg80211_up(net))) {DHD_ERROR(("%s: failed to bring up cfg80211\n", __FUNCTION__));ret = -1;goto exit;}if (!dhd_download_fw_on_driverload) {#ifdef ARP_OFFLOAD_SUPPORTdhd->pend_ipaddr = 0;if (!dhd_inetaddr_notifier_registered) {dhd_inetaddr_notifier_registered = TRUE;register_inetaddr_notifier(&dhd_inetaddr_notifier);}
#endif /* ARP_OFFLOAD_SUPPORT */
#if defined(CONFIG_IPV6) && defined(IPV6_NDO_SUPPORT)if (!dhd_inet6addr_notifier_registered) {dhd_inet6addr_notifier_registered = TRUE;register_inet6addr_notifier(&dhd_inet6addr_notifier);}
#endif /* CONFIG_IPV6 && IPV6_NDO_SUPPORT */}argos_register_notifier_init(net);
#if defined(NUM_SCB_MAX_PROBE)dhd_set_scb_probe(&dhd->pub);
#endif /* NUM_SCB_MAX_PROBE */
#endif /* WL_CFG80211 */}/* Allow transmit calls */netif_start_queue(net);dhd->pub.up = 1;OLD_MOD_INC_USE_COUNT;#ifdef BCMDBGFSdhd_dbgfs_init(&dhd->pub);
#endifexit:if (ret) {dhd_stop(net);}DHD_PERIM_UNLOCK(&dhd->pub);DHD_OS_WAKE_UNLOCK(&dhd->pub);DHD_MUTEX_UNLOCK();printf("%s: Exit ret=%d\n", __FUNCTION__, ret);return ret;
}

这个方法是真的长,到此就找到了上面热点打不开报错的位置:

 if (!dhd_download_fw_on_driverload) {if (!dhd_driver_init_done) {DHD_ERROR(("%s: WLAN driver is not initialized\n", __FUNCTION__));return -1;}}

那么dhd_driver_init_done是在什么时候被设置为true的呢,看这个类里面的dhd_module_init方法,也就是说在第7点startHal()方法;流程中被置为true的,而之前有分析了,startHal()成功的判断条件是/proc/net/dev节点中存在wlan0,那么什么时候开始这个判断成立的呢,看dhd_module_init(void)register_reboot_notifier(&dhd_reboot_notifier),在这个方法流程中就会初始化wlan0,那么这就导致了,同步问题,wlan0虽然初始化完成了,但dhd_driver_init_done还未被设置,导致上面的报错

解决办法

为 **dhd_module_init(void)dhd_open(struct net_device *net)**这两个方法添加互斥锁,已解决同步问题,如下:

DEFINE_MUTEX(_wlan_init_mutex_lock_);
#define WLAN_MUTEX_LOCK() \do { \if (mutex_is_locked(&_wlan_init_mutex_lock_) == 0) { \printf("%s : no wlan mutex held. set lock\n", __FUNCTION__); \} else { \printf("%s : wlan mutex is locked!. wait for unlocking\n", __FUNCTION__); \} \mutex_lock(&_wlan_init_mutex_lock_); \} while (0)#define WLAN_MUTEX_UNLOCK() \do { \mutex_unlock(&_wlan_init_mutex_lock_); \printf("%s : the wlan lock is released.\n", __FUNCTION__); \} while (0)
    if (!dhd_download_fw_on_driverload) {WLAN_MUTEX_LOCK();if (!dhd_driver_init_done) {DHD_ERROR(("%s: WLAN driver is not initialized\n", __FUNCTION__));WLAN_MUTEX_UNLOCK();return -1;}else{WLAN_MUTEX_UNLOCK();}}
static int
dhd_module_init(void)
{int err;int retry = 0;WLAN_MUTEX_LOCK();printf("%s: in %s\n", __FUNCTION__, dhd_version);DHD_PERIM_RADIO_INIT();....................................printf("%s: Exit err=%d\n", __FUNCTION__, err);WLAN_MUTEX_UNLOCK();return err;
}

记一次rk平台热点打开流程追踪记录相关推荐

  1. android8.0热点打开流程,从Setting到framework

    关键函数的调用流程如下: 热点的开启能否成功,主要由以下几个因素影响 (1)WifiStateMachine.java中的状态异常,而引起状态异常的原因有很多,可能是由于我们在修改的过程中,发生了一些 ...

  2. 一个简单的wifi热点启动流程追踪

    文章目录 一.ACS初始化函数 二.函数跳转驱动 最近在调试wifi的热点相关的东西,做下笔记.代码根据log进行调用追踪. 之前说过,我在关闭CONFIG_ACS的开关后,可以实现热点的打开.但我尝 ...

  3. 超图平台倾斜摄影发布流程

    超图平台倾斜摄影发布流程 1.前言 下文介绍了倾斜摄影数据在超图平台生成配置文件,利用配置文件将倾斜摄影从OSGB格式转成S3M格式,最终将S3M格式发布成iserver服务的详细流程.倾斜摄影OSG ...

  4. 高通SM4350平台指纹移植流程

    本文总结了高通sm4350平台指纹移植流程,厂家一般会提供移植文档,本文档可作为补充: 准备工作: 1.把指纹模组扣到主板上的SPI连接器上 2.高通sm4350平台,Android R版本全代码,全 ...

  5. 全志linux关机键,全志平台linux启动流程分析

    转载:全志平台linux启动流程分析 一.BROM阶段 机器上电之后会执行固化在BROM里面的一段引导程序,这个程序会依次遍历所有支持的启动介质,直到找到第一个支持的.目前支持的启动介质是sd/mmc ...

  6. RK平台--EVS模块: (一)概述

    最近在做车机camera相关的工作,由于手头上没有车载soc的板子,想着用RK3399实现car的EVS功能来熟悉一下android的Automotive 服务. 平台: 硬件soc--RK3399 ...

  7. [Camera]RK平台摄像头驱动

    platform:rk3399 OS:Android 7.1 Kernel:4.4 参考: 1. KrisFei https://blog.csdn.net/kris_fei/article/deta ...

  8. RK 平台MIPI 点屏注意事项

    转自:https://www.cnblogs.com/chorm590/p/11658360.html rk 平台关于 MIPI 屏幕的点屏流程已经非常完善了,基本上只要确定了硬件没问题.接线没问题. ...

  9. (七十一)Android O WiFi热点 开启流程梳理

    前言:之前主要梳理了WiFi开启扫描连接的流程,现在梳理下WiFi 热点 的开启流程. 时序图mdj样式:https://download.csdn.net/download/sinat_200594 ...

最新文章

  1. mysql库可以无限创建吗_mysql 创建库
  2. 程序员在外面看见bug会想修吗? | 每日趣闻
  3. python2.7 升级到 python3.6
  4. MySQL中各种字段的取值范围
  5. 网桥(bridge) 和 交换机(switch) 之异同
  6. c 最大子序列和_最大连续子序列
  7. Cloudflare通过集成ENS和IPFS推出通往分布式Web的网关
  8. model.train()和model.eval()
  9. IEEE MAC地址分配
  10. RGB颜色对照表(数值+英文,Markdown可用)
  11. cad卸载_想重新安装CAD提示已经安装?不会卸载?进来教你卸载CAD
  12. 日常开发中linux中最常用的100条命令
  13. developer.biao.daily.20140628
  14. 月中工作总结_在全职工作的9个月中,我是如何从新手转到软件工程师的
  15. 老钓友分享蚯蚓钓鲤鱼配方
  16. 【蓝桥杯】【Python】次数差
  17. 回锅肉飘香,《Pokémon GO》再度成为最卖座的iPhone游戏
  18. 【功能实现】qrcode生成二维码Demo
  19. 白宫举办开源安全峰会,众多科技巨头参加
  20. codeforce Gym 100685E Epic Fail of a Genie(MaximumProduction 贪心)

热门文章

  1. 6种方法计算神经网络参数量Params、计算量FLOPs、Macs简单代码
  2. 如何解决读写txt文件中文乱码问题
  3. 01背包, 完全背包,多重背包
  4. 计算机DNS怎么配置,如何设置电脑的dns地址
  5. win10安装linux虚拟机
  6. 洛谷 P3975 [TJOI2015]弦论 解题报告
  7. vue给html加背景图,Vue背景图如何全屏显示
  8. 删除docker registry镜像脚本报错No repositories directory found inside REGISTRY_DATA_DIR
  9. [team]开发中的“最速曲线”
  10. 10个值得珍藏的4K高清壁纸网站推荐