精华内容
下载资源
问答
  • Latency-Based WiFi Congestion Control in the Air for Dense WiFi Networks
  • WiFi 6E与WiFi7技术特点

    千次阅读 2020-12-19 15:23:13
    WiFi6还没有完全普及,WiFi6E与WiFi7已经开始在规划了。技术的更新换代速度超乎想象。 WiFi的极速传输基于超宽的带宽,而WiFi6E的带宽将新增1200MHz! 增加的带宽可以有更多的非重叠信道、更加干净的频谱、更加高...

    WiFi6还没有完全普及,WiFi6E与WiFi7已经开始在规划了。技术的更新换代速度超乎想象。
    WiFi的极速传输基于超宽的带宽,而WiFi6E的带宽将新增1200MHz!
    在这里插入图片描述
    增加的带宽可以有更多的非重叠信道、更加干净的频谱、更加高性能的终端、传输延时更低。
    在这里插入图片描述
    更多的频谱资源可以使得WLAN Router具有更灵活的频谱搭配,满足家庭的各种WLAN应用。
    在这里插入图片描述
    全球批准WiFi6E频段的时间节点以及定义的功率大小。

    展开全文
  • 由于最新的Windows 8对于WiFi 传输有Latency的要求,最近一直在忙这件事。 MS要求Peer到Peer之间的Lantency要小于几十个毫秒吧。这段时间可以划分为一下这几个部分: APP-->Driver--(Air)-->Driver in Rx ...

    由于最新的Windows 8对于WiFi 传输有Latency的要求,最近一直在忙这件事。

    MS要求Peer到Peer之间的Lantency要小于几十个毫秒吧。这段时间可以划分为一下这几个部分:

    APP-->Driver--(Air)-->Driver in Rx side-->APP in Rx side。

    我发现在Receiver这一端,经常会发生Driver Indicate Packet给OS到APP收到这个包,有时候会有大的延时。

    因为用的是UDP,理论上应该没有window,sequence number这种概念,OS只要收到一个数据包就应该通知APP。

     根据Driver的现状一开始以为可能跟下面这些因素有关:

    1.Resource,因为Driver Pre-allocated的一些memory有可能会不够。

    2.Disorder,Driver发送IP layer的数据包,有时候存在乱序。

    但是Debug,fix了1,2这两个问题,latency还是很大,有几十ms.

    终于在下班前想到一点,就是APP本身的问题,APP是一个同事写的,下面简单描述下Rx端的伪代码:

    config socket...
    for
    (;;){ WSARecvFrom(socket,...); ... Do something. ... }

    发现Latency其实就在于Do something花费了一段可观的时间。代码的逻辑导致APP需要逐个处理数据包,Do something需要花费大概2ms。也就是说1s最多能handle500个Packet,但是Driver有时在1s内会indicate将近800个Packet给OS。所以导致APP这边存在瓶颈,OS只有等APP处理玩当前数据包,才传下一个给APP。

    To fix,就是要尽量缩短Do something暂用的时间。APP的架构需要变一下,用两个Thread,一个只管收,占用时间要短,另一个做Do something的处理。

    BTW,介绍下Windows精确计时的方法:

    UserMode: QueryPerformanceCounter()/QueryPerformanceFrequency();

    KernelMode: KeQueryPerformanceCounter(&PerformanceFrequency);

    用上面这几个函数就可以了,具体用法见MSDN。

    转载于:https://www.cnblogs.com/zzSoftware/archive/2013/02/26/2934189.html

    展开全文
  • wifi_error wifi_get_supported_feature_set(wifi_interface_handle iface, feature_set *set) wifi_error wifi_set_latency_mode(wifi_interface_handle handle, wifi_latency_mode mode) 在低延迟模式下,Android...

    Android 10 扩展了 WLAN Lock API,以允许容易受到延迟时间影响的应用将 WLAN 配置为低延迟模式。当满足以下所有条件时,低延迟模式便会启动:

    已启用 WLAN,设备可以访问互联网。

    应用已创建并获得 WLAN 锁,并且正在前台运行。

    屏幕处于开启状态。

    要在设备上支持低延迟模式,设备制造商必须更新 WLAN 驱动程序和供应商 HAL。在低延迟模式下,框架会明确停用节电模式(在 IEEE 802.11 标准中也称为低电耗模式状态)。您可以优化驱动程序层和固件层中的扫描和漫游参数,以进一步降低 WLAN 延迟。确切的优化项目因实现而异。

    Android 具有高性能 WLAN 锁定模式(在 API 级别 12 中引入),它与低延迟模式相互独立。

    实现

    getCapabilities_1_3() generates (WifiStatus status,

    bitfield capabilities)

    setLatencyMode(LatencyMode mode) generates (WifiStatus status)

    参考实现可以在 wifi_legacy_hal.cpp 中找到,其中包含以下函数:

    wifi_error wifi_get_supported_feature_set(wifi_interface_handle

    iface, feature_set *set)

    wifi_error wifi_set_latency_mode(wifi_interface_handle handle,

    wifi_latency_mode mode)

    在低延迟模式下,Android 框架中的 WifiLockManager 会明确停用节电模式。要支持此行为,WLAN 驱动程序必须支持 NL80211 命令 NL80211_CMD_SET_POWER_SAVE,以启用和停用节电模式。停用 WLAN 节电模式后,WLAN 系统必须保持唤醒状态,并可以开始以最低延迟发送或接收数据包。

    注意:低延迟模式在支持 android.hardware.wifi@1.3 的设备上完全受支持。对于搭载 Android 10 但不支持 android.hardware.wifi@1.3 的设备,仅当应用获得低延迟模式 WLAN 锁且该锁处于启用状态时,WifiLockManager 才会停用节电模式。

    停用此功能

    要关闭低延迟模式功能,请更新 getCapabilities_1_3() 的底层代码,使得 capabilities & SET_LATENCY_MODE = 0,其中 SET_LATENCY_MODE 在 IWifiChip.hal 中定义。停用此功能后,仅当低延迟模式处于启用状态时,框架才会停用节电模式。

    验证

    要测试低延迟模式在启用状态下的工作情况,请运行以下自动测试和手动 ping 延迟时间测试。

    自动测试

    运行以下 VTS 和 CTS 测试:

    手动测试

    所需的测试设备和环境

    对于手动测试,需要进行以下设置:

    WLAN 接入点 (AP)

    被测设备 (DUT) 手机和测试计算机

    DUT 必须通过 WLAN 连接到接入点。

    测试计算机必须通过 WLAN 或以太网连接到接入点。

    测试计算机必须通过 USB 连接到 DUT。

    上行链路 ping 测试

    启用低延迟模式。

    adb root

    adb shell cmd wifi force-low-latency-mode enabled

    确保您的计算机通过 ADB 与手机连接。通过 ADB shell,以 1 秒的间隔连续对网关执行 3 小时 ping 操作。

    将测试输出保存在文本文件中,并使用电子表格或 Python 脚本生成 ping 延迟时间测试结果的直方图。

    在停用延迟模式的情况下重复执行第 1-3 步。

    adb root

    adb shell cmd wifi force-low-latency-mode disabled

    比较测试结果,以确保低延迟模式处于启用状态时平均 ping 延迟值有所降低。

    注意:(可选)要确定端到端延迟时间总体上是否有所降低,请使用知名的主机地址(如 google.com)重复测试。

    下行链路 ping 测试

    启用低延迟模式。

    adb root

    adb shell cmd wifi force-low-latency-mode enabled

    通过测试计算机的命令行,以 1 秒的间隔连续对手机的 IP 地址执行 3 小时 ping 操作。

    将测试输出保存在文本文件中,并使用电子表格或 Python 脚本生成 ping 延迟时间测试结果的直方图。

    在停用延迟模式的情况下重复执行第 1-3 步。

    adb root

    adb shell cmd wifi force-low-latency-mode disabled

    比较测试结果,以确保低延迟模式处于启用状态时平均 ping 延迟值有所降低。

    注意:(可选)要确定端到端延迟时间是否有所降低,请使用知名的主机地址(如 google.com)重复 ping 测试。

    其他测试

    在不同的环境中重复上述测试。例如,您可以在家里或办公室进行测试。

    展开全文
  •  这次接着讲Wifi工程流程中的Wifi热点查找过程,也是Wifi启动的过程延续,Wifi启动过程中会更新Wifi的状态,框架层也有相应广播发出,应用层接收到广播后开始进行热点的扫描。可以先看前面Wifi启动的分析过程。  ...

    一 前言

           这次接着讲Wifi工程流程中的Wifi热点查找过程,也是Wifi启动的过程延续,Wifi启动过程中会更新Wifi的状态,框架层也有相应广播发出,应用层接收到广播后开始进行热点的扫描。可以先看前面Wifi启动的分析过程。

                                   Wifi模块—源码分析Wifi启动1(Android P)

                                   Wifi模块—源码分析Wifi启动2(Android P)

     

    二 图示调用流程

          由于在 frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java直接接收到框架层发出的wifi状态改变的广播WIFI_STATE_CHANGED_ACTION(这个在后面有交待),所以这里的图示调用流程将从WifiTracker.java开始。

    三 代码具体流程

           我们先回顾一下之前wifi启动过程的相关细节,wifi启动过程会走到ClientModeStateMachine这个状态机,具体看 Wifi模块—源码分析Wifi启动1(Android P)

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

    看ClientModeStateMachine的构造函数。

    ClientModeStateMachine(Looper looper) {
        super(TAG, looper);
    
        addState(mIdleState);
        addState(mStartedState);
    
        setInitialState(mIdleState);
        start();
    }

    再看start()。

    /**
    * Start client mode.
    */
    public void start() {
        mStateMachine.sendMessage(ClientModeStateMachine.CMD_START);
    }

    发送了一个消息ClientModeStateMachine.CMD_START。

    private class IdleState extends State {
    
        @Override
        public void enter() {
            Log.d(TAG, "entering IdleState");
            mClientInterfaceName = null;
            mIfaceIsUp = false;
        }
    
        @Override
        public boolean processMessage(Message message) {
            switch (message.what) {
                case CMD_START:
                    updateWifiState(WifiManager.WIFI_STATE_ENABLING,
                                        WifiManager.WIFI_STATE_DISABLED);
    
                    mClientInterfaceName = mWifiNative.setupInterfaceForClientMode(false /* not low priority */, mWifiNativeInterfaceCallback);
                    if (TextUtils.isEmpty(mClientInterfaceName)) {
                        Log.e(TAG, "Failed to create ClientInterface. Sit in Idle");
                        updateWifiState(WifiManager.WIFI_STATE_UNKNOWN,
                                    WifiManager.WIFI_STATE_ENABLING);
                        updateWifiState(WifiManager.WIFI_STATE_DISABLED,
                                            WifiManager.WIFI_STATE_UNKNOWN);
                        break;
                    }
                    sendScanAvailableBroadcast(false);
                    mScanRequestProxy.enableScanningForHiddenNetworks(false);
                    mScanRequestProxy.clearScanResults();
                    transitionTo(mStartedState);
                    break;
                default:
                    Log.d(TAG, "received an invalid message: " + message);
                    return NOT_HANDLED;
            }
            return HANDLED;
        }
    }

    在IdleState的状态里接收到消息做相关处理updateWifiState,继续看这个方法。

    /**
         * Update Wifi state and send the broadcast.
         * @param newState new Wifi state
         * @param currentState current wifi state
    */
    private void updateWifiState(int newState, int currentState) {
        if (!mExpectedStop) {
            mListener.onStateChanged(newState);
        } else {
            Log.d(TAG, "expected stop, not triggering callbacks: newState = "
                + newState);
        }
    
        // Once we report the mode has stopped/failed any other stop signals are redundant
        // note: this can happen in failure modes where we get multiple callbacks as underlying
        // components/interface stops or the underlying interface is destroyed in cleanup
        if (newState == WifiManager.WIFI_STATE_UNKNOWN
                    || newState == WifiManager.WIFI_STATE_DISABLED) {
            mExpectedStop = true;
        }
    
        if (newState == WifiManager.WIFI_STATE_UNKNOWN) {
            // do not need to broadcast failure to system
            return;
        }
    
        mWifiStateMachine.setWifiStateForApiCalls(newState);
    
        final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
        intent.putExtra(WifiManager.EXTRA_WIFI_STATE, newState);
        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, currentState);
        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
    }

    在这里发送了一个广播sendStickyBroadcastAsUser,广播具体是WifiManager.WIFI_STATE_CHANGED_ACTION。

            OK,上面的分析都属于上次分析wifi启动的部分,就是wifi在启动过程中会更新wifi状态并发送wifi状态改变的广播。不过。上次在分析wifi启动过程中也没有分析这个更新wifi状态发送广播的这个内容,这次作为一个补充也是开启接下来的wifi热点扫描的流程分析,接下来的wifi扫描过程是指打开wifi之后自动扫描过程而不是手动去刷新扫描。

     

    1 应用层

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

    在WifiSettings.java里初始化WifiTracker。

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    
        mWifiTracker = WifiTrackerFactory.create(
                getActivity(), this, getLifecycle(), true, true);
        mWifiManager = mWifiTracker.getManager();
        ...
    }

       

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

           应用层在扫描过程中貌似没做什么事情但暂且提一下WifiEnabler也会接收到WifiManager.WIFI_STATE_CHANGED_ACTION广播并会相关的处理。

    private boolean mStateMachineEvent;
    private final IntentFilter mIntentFilter;
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
                handleWifiStateChanged(mWifiManager.getWifiState());
            } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
                if (!mConnected.get()) {
                    handleStateChanged(WifiInfo.getDetailedStateOf((SupplicantState)
                            intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)));
                }
            } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
                NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
                        WifiManager.EXTRA_NETWORK_INFO);
                mConnected.set(info.isConnected());
                handleStateChanged(info.getDetailedState());
            }
        }
    };

    看handleWifiStateChanged。

    private void handleWifiStateChanged(int state) {
        // Clear any previous state
        mSwitchWidget.setDisabledByAdmin(null);
    
        switch (state) {
            case WifiManager.WIFI_STATE_ENABLING:
                break;
            case WifiManager.WIFI_STATE_ENABLED:
                setSwitchBarChecked(true);
                mSwitchWidget.setEnabled(true);
                break;
            case WifiManager.WIFI_STATE_DISABLING:
                break;
            case WifiManager.WIFI_STATE_DISABLED:
                setSwitchBarChecked(false);
                mSwitchWidget.setEnabled(true);
                break;
            default:
                setSwitchBarChecked(false);
                mSwitchWidget.setEnabled(true);
        }
    
        if (RestrictedLockUtils.hasBaseUserRestriction(mContext,
                UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.myUserId())) {
            mSwitchWidget.setEnabled(false);
        } else {
            final EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
                UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.myUserId());
            mSwitchWidget.setDisabledByAdmin(admin);
        }
    }

    根据wifi状态,switchBar的状态会变化。

     

    2 java框架层

         真正的扫描过程从这里开始。framework/base/packages/SettingsLib。SettingsLib放在frameworks/base/packages/SettingsLib,因为SystemUi和开机向导中的蓝牙WiFi流程也会用到对应的代码。

    2.1 frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java

    /**
    *  Receiver for handling broadcasts.
    *
    *  This receiver is registered on the WorkHandler.
    */
    @VisibleForTesting
    final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
    
            if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
                updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                                WifiManager.WIFI_STATE_UNKNOWN));
            } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
                mStaleScanResults = false;
    
                fetchScansAndConfigsAndUpdateAccessPoints();
            } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)
                    || WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
                fetchScansAndConfigsAndUpdateAccessPoints();
            } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
                // TODO(sghuman): Refactor these methods so they cannot result in duplicate
                // onAccessPointsChanged updates being called from this intent.
                NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
                updateNetworkInfo(info);
                fetchScansAndConfigsAndUpdateAccessPoints();
            } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
                NetworkInfo info =
                        mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
                updateNetworkInfo(info);
            }
        }
    };

    看updateWifiState。

    /**
    * Handles updates to WifiState.
    *
    * <p>If Wifi is not enabled in the enabled state, {@link #mStaleScanResults} will be set to
    * true.
    */
    private void updateWifiState(int state) {
        if (state == WifiManager.WIFI_STATE_ENABLED) {
            if (mScanner != null) {
                // We only need to resume if mScanner isn't null because
                // that means we want to be scanning.
                mScanner.resume();
            }
        } else {
            clearAccessPointsAndConditionallyUpdate();
            mLastInfo = null;
            mLastNetworkInfo = null;
            if (mScanner != null) {
                mScanner.pause();
            }
            mStaleScanResults = true;
        }
        mListener.onWifiStateChanged(state);
    }

    看mScanner.resume,其中Scanner是内部类。

    @VisibleForTesting
    class Scanner extends Handler {
        static final int MSG_SCAN = 0;
    
        private int mRetry = 0;
    
        void resume() {
            if (!hasMessages(MSG_SCAN)) {
                sendEmptyMessage(MSG_SCAN);
            }
        }
    
        void pause() {
            mRetry = 0;
            removeMessages(MSG_SCAN);
        }
    
        @VisibleForTesting
        boolean isScanning() {
            return hasMessages(MSG_SCAN);
        }
    
        @Override
        public void handleMessage(Message message) {
            if (message.what != MSG_SCAN) return;
            if (mWifiManager.startScan()) {
                mRetry = 0;
            } else if (++mRetry >= 3) {
                mRetry = 0;
                if (mContext != null) {
                    Toast.makeText(mContext, R.string.wifi_fail_to_scan, Toast.LENGTH_LONG).show();
                }
                return;
            }
            sendEmptyMessageDelayed(MSG_SCAN, WIFI_RESCAN_INTERVAL_MS);
        }
    }

           在resume方法里面发送消息MSG_SCAN,并接收该消息进行处理,看handleMessage。会调用mWifiManager.startScan,并且每隔10s再次进行扫描。

     

    2.2 frameworks/base/wifi/java/android/net/wifi/WifiManager.java

    * <p>
    * To initiate a Wi-Fi scan, declare the
    * {@link android.Manifest.permission#CHANGE_WIFI_STATE}
    * permission in the manifest, and perform these steps:
    * </p>
    * <ol style="1">
    * <li>Invoke the following method:
    * {@code ((WifiManager) getSystemService(WIFI_SERVICE)).startScan()}</li>
    * <li>
    * Register a BroadcastReceiver to listen to
    * {@code SCAN_RESULTS_AVAILABLE_ACTION}.</li>
    * <li>When a broadcast is received, call:
    * {@code ((WifiManager) getSystemService(WIFI_SERVICE)).getScanResults()}</li>
    * </ol>
    * @return {@code true} if the operation succeeded, i.e., the scan was initiated.
    * @deprecated The ability for apps to trigger scan requests will be removed in a future
    * release.
    */
    @Deprecated
    public boolean startScan() {
        return startScan(null);
    }
    
    /** @hide */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
    public boolean startScan(WorkSource workSource) {
        try {
            String packageName = mContext.getOpPackageName();
            return mService.startScan(packageName);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    

    看mService.startScan。这个mService是远程的服务端WifiService,其实现类为WifiServiceImpl,这个过程也是跨进程调用。

     

    2.3  frameworks/opt/net/wifi/service/java/com/android/server/wifi/wifiServiceImpl.java

    /**
    * See {@link android.net.wifi.WifiManager#startScan}
    *
    * @param packageName Package name of the app that requests wifi scan.
    */
    @Override
    public boolean startScan(String packageName) {
        if (enforceChangePermission(packageName) != MODE_ALLOWED) {
            return false;
        }
    
        int callingUid = Binder.getCallingUid();
        long ident = Binder.clearCallingIdentity();
        mLog.info("startScan uid=%").c(callingUid).flush();
        synchronized (this) {
            if (mInIdleMode) {
                // Need to send an immediate scan result broadcast in case the
                // caller is waiting for a result ..
    
                // TODO: investigate if the logic to cancel scans when idle can move to
                // WifiScanningServiceImpl.  This will 1 - clean up WifiServiceImpl and 2 -
                // avoid plumbing an awkward path to report a cancelled/failed scan.  This will
                // be sent directly until b/31398592 is fixed.
                sendFailedScanBroadcast();
                mScanPending = true;
                return false;
            }
        }
        try {
            mWifiPermissionsUtil.enforceCanAccessScanResults(packageName, callingUid);
            Mutable<Boolean> scanSuccess = new Mutable<>();
            boolean runWithScissorsSuccess = mWifiInjector.getWifiStateMachineHandler()
                    .runWithScissors(() -> {
                        scanSuccess.value = mScanRequestProxy.startScan(callingUid, packageName);
                    }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS);
            if (!runWithScissorsSuccess) {
                Log.e(TAG, "Failed to post runnable to start scan");
                sendFailedScanBroadcast();
                return false;
            }
            if (!scanSuccess.value) {
                Log.e(TAG, "Failed to start scan");
                return false;
            }
        } catch (SecurityException e) {
            return false;
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        return true;
    }

    继续看mScanRequestProxy.startScan。

     

    2.4  frameworks/opt/net/wifi/service/java/com/android/server/wifi/ScanRequestProxy.java

    /**
    * Initiate a wifi scan.
    *
    * @param callingUid The uid initiating the wifi scan. Blame will be given to this uid.
    * @return true if the scan request was placed or a scan is already ongoing, false otherwise.
    */
    public boolean startScan(int callingUid, String packageName) {
        if (!retrieveWifiScannerIfNecessary()) {
            Log.e(TAG, "Failed to retrieve wifiscanner");
            sendScanResultFailureBroadcastToPackage(packageName);
            return false;
        }
        boolean fromSettingsOrSetupWizard =
                mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid)
                        || mWifiPermissionsUtil.checkNetworkSetupWizardPermission(callingUid);
        // Check and throttle scan request from apps without NETWORK_SETTINGS permission.
        if (!fromSettingsOrSetupWizard
                && shouldScanRequestBeThrottledForApp(callingUid, packageName)) {
            Log.i(TAG, "Scan request from " + packageName + " throttled");
            sendScanResultFailureBroadcastToPackage(packageName);
            return false;
        }
        // Create a worksource using the caller's UID.
        WorkSource workSource = new WorkSource(callingUid);
    
        // Create the scan settings.
        WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
        // Scan requests from apps with network settings will be of high accuracy type.
        if (fromSettingsOrSetupWizard) {
            settings.type = WifiScanner.TYPE_HIGH_ACCURACY;
        }
        // always do full scans
        settings.band = WifiScanner.WIFI_BAND_BOTH_WITH_DFS;
        settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
                | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
        if (mScanningForHiddenNetworksEnabled) {
            // retrieve the list of hidden network SSIDs to scan for, if enabled.
            List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworkList =
                    mWifiConfigManager.retrieveHiddenNetworkList();
            settings.hiddenNetworks = hiddenNetworkList.toArray(
                    new WifiScanner.ScanSettings.HiddenNetwork[hiddenNetworkList.size()]);
        }
        mWifiScanner.startScan(settings, new ScanRequestProxyScanListener(), workSource);
        mIsScanProcessingComplete = false;
        return true;
    }

    看mWifiScanner.startScan

     

    2.5  frameworks/base/wifi/java/android/net/wifi/WifiScanner.java

    /**
    * starts a single scan and reports results asynchronously
    * @param settings specifies various parameters for the scan; for more information look at
    * {@link ScanSettings}
    * @param listener specifies the object to report events to. This object is also treated as a
    *                key for this scan, and must also be specified to cancel the scan. Multiple
    *           scans should also not share this object.
    */
    @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
    public void startScan(ScanSettings settings, ScanListener listener) {
        startScan(settings, listener, null);
    }
    
    /**
    * starts a single scan and reports results asynchronously
    * @param settings specifies various parameters for the scan; for more information look at
    * {@link ScanSettings}
    * @param workSource WorkSource to blame for power usage
    * @param listener specifies the object to report events to. This object is also treated as a
    *                 key for this scan, and must also be specified to cancel the scan. Multiple
    *                 scans should also not share this object.
    */
    @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
    public void startScan(ScanSettings settings, ScanListener listener, WorkSource workSource) {
        Preconditions.checkNotNull(listener, "listener cannot be null");
        int key = addListener(listener);
        if (key == INVALID_KEY) return;
        validateChannel();
        Bundle scanParams = new Bundle();
        scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
        scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
        mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
        
    }

            Android P和Android O框架层代码改变不是很大,不过Android O的操作基本都是经过WifiStateMachine状态机,而Android P很多操作都不再经过WifiStateMachine状态机,在这里是为了把scan功能独立分开出来。而和Android O之前的代码相比则有很大区别,这里接下来使用到了双向异步通道的方式,mAsyncChannel.sendMessage。AsyncChannel处理两个handler之间消息异步传递的问题,这两个handler可以在一个进程也可以处于不同的进程。 

     

    2.6  frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java

    private class ClientHandler extends WifiHandler {
    
        ClientHandler(String tag, Looper looper) {
            super(tag, looper);
        }
    
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            ...
            switch (msg.what) {
                case WifiScanner.CMD_START_BACKGROUND_SCAN:
                case WifiScanner.CMD_STOP_BACKGROUND_SCAN:
                    mBackgroundScanStateMachine.sendMessage(Message.obtain(msg));
                    break;
                case WifiScanner.CMD_START_PNO_SCAN:
                case WifiScanner.CMD_STOP_PNO_SCAN:
                    mPnoScanStateMachine.sendMessage(Message.obtain(msg));
                    break;
                case WifiScanner.CMD_START_SINGLE_SCAN:
                case WifiScanner.CMD_STOP_SINGLE_SCAN:
                    mSingleScanStateMachine.sendMessage(Message.obtain(msg));
                    break;
                ...
            }
        }
    }

       ClientHandler接收到CMD_START_SINGLE_SCAN消息并处理也就是向状态机发送这个消息,mSingleScanStateMachine.sendMessage(Message.obtain(msg))。SingleScanStateMachine是个处理热点扫描的状态机。

    /**
    * State machine that holds the state of single scans. Scans should only be active in the
    * ScanningState. The pending scans and active scans maps are swapped when entering
    * ScanningState. Any requests queued while scanning will be placed in the pending queue and
    * executed after transitioning back to IdleState.
    */
    class WifiSingleScanStateMachine extends StateMachine implements WifiNative.ScanEventHandler {
        /**
        * Maximum age of results that we return from our cache via
        * {@link WifiScanner#getScanResults()}.
        * This is currently set to 3 minutes to restore parity with the wpa_supplicant's scan
        * result cache expiration policy. (See b/62253332 for details)
        */
        @VisibleForTesting
        public static final int CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS = 180 * 1000;
    
        private final DefaultState mDefaultState = new DefaultState();
        private final DriverStartedState mDriverStartedState = new DriverStartedState();
        private final IdleState  mIdleState  = new IdleState();
        private final ScanningState  mScanningState  = new ScanningState();
    
        private WifiNative.ScanSettings mActiveScanSettings = null;
        private RequestList<ScanSettings> mActiveScans = new RequestList<>();
        private RequestList<ScanSettings> mPendingScans = new RequestList<>();
    
        // Scan results cached from the last full single scan request.
        private final List<ScanResult> mCachedScanResults = new ArrayList<>();
    
        WifiSingleScanStateMachine(Looper looper) {
            super("WifiSingleScanStateMachine", looper);
    
            setLogRecSize(128);
            setLogOnlyTransitions(false);
    
            // CHECKSTYLE:OFF IndentationCheck
            addState(mDefaultState);
                addState(mDriverStartedState, mDefaultState);
                    addState(mIdleState, mDriverStartedState);
                    addState(mScanningState, mDriverStartedState);
                // CHECKSTYLE:ON IndentationCheck
    
                setInitialState(mDefaultState);
        }
        ...
    }
    

    状态机有四个状态,初始状态为默认状态。在DriverStarted对CMD_START_SINGLE_SCAN消息进行处理。

    /**
    * State representing when the driver is running. This state is not meant to be transitioned
    * directly, but is instead intended as a parent state of ScanningState and IdleState
    * to hold common functionality and handle cleaning up scans when the driver is shut down.
    */
    class DriverStartedState extends State {
        ...
    
        @Override
        public boolean processMessage(Message msg) {
            ClientInfo ci = mClients.get(msg.replyTo);
    
            switch (msg.what) {
                case CMD_DRIVER_LOADED:
                    // Ignore if we're already in driver loaded state.
                    return HANDLED;
                case WifiScanner.CMD_START_SINGLE_SCAN:
                    mWifiMetrics.incrementOneshotScanCount();
                    int handler = msg.arg2;
                    Bundle scanParams = (Bundle) msg.obj;
                    if (scanParams == null) {
                        logCallback("singleScanInvalidRequest",  ci, handler, "null params");
                        replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
                        return HANDLED;
                    }
                    scanParams.setDefusable(true);
                    ScanSettings scanSettings =
                                scanParams.getParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY);
                    WorkSource workSource =
                                scanParams.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY);
                    if (validateScanRequest(ci, handler, scanSettings)) {
                                logScanRequest("addSingleScanRequest", ci, handler, workSource,
                                        scanSettings, null);
                        replySucceeded(msg);
    
                        // If there is an active scan that will fulfill the scan request then
                        // mark this request as an active scan, otherwise mark it pending.
                        // If were not currently scanning then try to start a scan. Otherwise
                        // this scan will be scheduled when transitioning back to IdleState
                        // after finishing the current scan.
                        if (getCurrentState() == mScanningState) {
                            if (activeScanSatisfies(scanSettings)) {
                                mActiveScans.addRequest(ci, handler, workSource, scanSettings);
                            } else {
                                mPendingScans.addRequest(ci, handler, workSource, scanSettings);
                            }
                        } else {
                            mPendingScans.addRequest(ci, handler, workSource, scanSettings);
                            tryToStartNewScan();
                        }
                    } else {
                    ...
                    }    
            }
        }   
    }

    看调用的tryToStartNewScan。

    void tryToStartNewScan() {
        if (mPendingScans.size() == 0) { // no pending requests
            return;
        }
        ...
        if (mScannerImpl.startSingleScan(settings, this)) {
            // store the active scan settings
            mActiveScanSettings = settings;
            // swap pending and active scan requests
            RequestList<ScanSettings> tmp = mActiveScans;
            mActiveScans = mPendingScans;
            mPendingScans = tmp;
            // make sure that the pending list is clear
            mPendingScans.clear();
            transitionTo(mScanningState);
        } else {
            mWifiMetrics.incrementScanReturnEntry(
                    WifiMetricsProto.WifiLog.SCAN_UNKNOWN, mPendingScans.size());
            // notify and cancel failed scans
            sendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED,
                    "Failed to start single scan");
        }
    }

    看mScannerImpl.startSingleScan。

     

    2.7  frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java

    /**
    * Start a one time scan. This method should only be called when there is no scan going on
    * (after a callback indicating that the previous scan succeeded/failed).
    * @return if the scan paramaters are valid
    * Note this may return true even if the parameters are not accepted by the chip because the
    * scan may be scheduled async.
    */
    public abstract boolean startSingleScan(WifiNative.ScanSettings settings,
                     WifiNative.ScanEventHandler eventHandler);

     

    2.8  frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java

    WificondScannerImpl.java继承抽象类WifiScannerImpl.java,所以复写了startSingleScan。

    @Override
    public boolean startSingleScan(WifiNative.ScanSettings settings,
            WifiNative.ScanEventHandler eventHandler) {
        ...
        if (!allFreqs.isEmpty()) {
            freqs = allFreqs.getScanFreqs();
            success = mWifiNative.scan(
                            mIfaceName, settings.scanType, freqs, hiddenNetworkSSIDSet);
            if (!success) {
                Log.e(TAG, "Failed to start scan, freqs=" + freqs);
            }
        } else {
            // There is a scan request but no available channels could be scanned for.
            // We regard it as a scan failure in this case.
            Log.e(TAG, "Failed to start scan because there is no available channel to scan");
        }
        if (success) {
            if (DBG) {
                Log.d(TAG, "Starting wifi scan for freqs=" + freqs);
            }
    
            mScanTimeoutListener = new AlarmManager.OnAlarmListener() {
                @Override public void onAlarm() {
                    handleScanTimeout();
                }
            };
    
            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                        mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS,
                            TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler);
            } else {
                // indicate scan failure async
                mEventHandler.post(new Runnable() {
                    @Override public void run() {
                        reportScanFailure();
                    }
                });
            }
    
            return true;
        }
    }

    看 mWifiNative.scan,java框架层最终还是会调用到native层。

     

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

    /**
    * Start a scan using wificond for the given parameters.
    * @param ifaceName Name of the interface.
    * @param scanType Type of scan to perform. One of {@link ScanSettings#SCAN_TYPE_LOW_LATENCY},
    * {@link ScanSettings#SCAN_TYPE_LOW_POWER} or {@link ScanSettings#SCAN_TYPE_HIGH_ACCURACY}.
    * @param freqs list of frequencies to scan for, if null scan all supported channels.
    * @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
    * @return Returns true on success.
    */
    public boolean scan(@NonNull String ifaceName, int scanType, Set<Integer> freqs,
                Set<String> hiddenNetworkSSIDs) {
        return mWificondControl.scan(ifaceName, scanType, freqs, hiddenNetworkSSIDs);
    }

    看mWificondControl.scan。

     

    2.10  frameworks/opt/net/wifi/service/java/com/android/server/wifi/WificondControl.java

    /**
    * Start a scan using wificond for the given parameters.
    * @param ifaceName Name of the interface.
    * @param scanType Type of scan to perform.
    * @param freqs list of frequencies to scan for, if null scan all supported channels.
    * @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
    * @return Returns true on success.
    */
    public boolean scan(@NonNull String ifaceName,
                        int scanType,
                        Set<Integer> freqs,
                        Set<String> hiddenNetworkSSIDs) {
        IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
        if (scannerImpl == null) {
            Log.e(TAG, "No valid wificond scanner interface handler");
            return false;
        }
        SingleScanSettings settings = new SingleScanSettings();
        try {
            settings.scanType = getScanType(scanType);
        } catch (IllegalArgumentException e) {
            Log.e(TAG, "Invalid scan type ", e);
            return false;
        }
        settings.channelSettings  = new ArrayList<>();
        settings.hiddenNetworks  = new ArrayList<>();
    
        if (freqs != null) {
            for (Integer freq : freqs) {
                ChannelSettings channel = new ChannelSettings();
                channel.frequency = freq;
                settings.channelSettings.add(channel);
            }
        }
        if (hiddenNetworkSSIDs != null) {
            for (String ssid : hiddenNetworkSSIDs) {
                HiddenNetwork network = new HiddenNetwork();
                try {
                    network.ssid = NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(ssid));
                } catch (IllegalArgumentException e) {
                    Log.e(TAG, "Illegal argument " + ssid, e);
                    continue;
                }
                settings.hiddenNetworks.add(network);
            }
        }
    
        try {
            return scannerImpl.scan(settings);
        } catch (RemoteException e1) {
            Log.e(TAG, "Failed to request scan due to remote exception");
        }
        return false;
    }

    又调到scannerImpl.scan了,看getScannerImpl。

    /** Helper function to look up the scanner impl handle using name */
    private IWifiScannerImpl getScannerImpl(@NonNull String ifaceName) {
        return mWificondScanners.get(ifaceName);
    }

    看mWificondScanners怎么来的,从下面可以看到wificondScanner = clientInterface.getWifiScannerImpl()。

    /**
    * Setup interface for client mode via wificond.
    * @return An IClientInterface as wificond client interface binder handler.
    * Returns null on failure.
    */
    public IClientInterface setupInterfaceForClientMode(@NonNull String ifaceName) {
        Log.d(TAG, "Setting up interface for client mode");
        if (!retrieveWificondAndRegisterForDeath()) {
            return null;
        }
    
        IClientInterface clientInterface = null;
        try {
            clientInterface = mWificond.createClientInterface(ifaceName);
        } catch (RemoteException e1) {
            Log.e(TAG, "Failed to get IClientInterface due to remote exception");
            return null;
        }
    
        if (clientInterface == null) {
            Log.e(TAG, "Could not get IClientInterface instance from wificond");
            return null;
        }
        Binder.allowBlocking(clientInterface.asBinder());
    
        // Refresh Handlers
        mClientInterfaces.put(ifaceName, clientInterface);
        try {
            IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl();
            if (wificondScanner == null) {
                Log.e(TAG, "Failed to get WificondScannerImpl");
                return null;
            }
            mWificondScanners.put(ifaceName, wificondScanner);
            Binder.allowBlocking(wificondScanner.asBinder());
            ScanEventHandler scanEventHandler = new ScanEventHandler(ifaceName);
            mScanEventHandlers.put(ifaceName,  scanEventHandler);
            wificondScanner.subscribeScanEvents(scanEventHandler);
            PnoScanEventHandler pnoScanEventHandler = new PnoScanEventHandler(ifaceName);
            mPnoScanEventHandlers.put(ifaceName,  pnoScanEventHandler);
            wificondScanner.subscribePnoScanEvents(pnoScanEventHandler);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to refresh wificond scanner due to remote exception");
        }
    
        return clientInterface;
    }

    看retrieveWificondAndRegisterForDeath。

    /**
    * Helper method to retrieve the global wificond handle and register for
    * death notifications.
    */
    private boolean retrieveWificondAndRegisterForDeath() {
        if (mWificond != null) {
            if (mVerboseLoggingEnabled) {
                Log.d(TAG, "Wificond handle already retrieved");
            }
            // We already have a wificond handle.
            return true;
        }
        mWificond = mWifiInjector.makeWificond();
        if (mWificond == null) {
            Log.e(TAG, "Failed to get reference to wificond");
            return false;
        }
        try {
            mWificond.asBinder().linkToDeath(this, 0);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to register death notification for wificond");
            // The remote has already died.
            return false;
        }
        return true;
    }
    

    看mWiifInjector.makeWificond。

     

    2.11  frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiInjector.java

    public IWificond makeWificond() {
        // We depend on being able to refresh our binder in WifiStateMachine, so don't cache it.
        IBinder binder = ServiceManager.getService(WIFICOND_SERVICE_NAME);
        return IWificond.Stub.asInterface(binder);
    }

    再往下走就是C++层了。往下的部分以后有机会再继续深入。

     

    四 总结

     wifi框架层从Android O版本开始变化还是挺大的,Android P是延续Android O 的改变做了一些不怎么大的变化:

          (1) 与其他Android版本相比,整体架构还好,多了wificond跟底层通信,framework整个修改比较大;

         (2) Android O  scan、scan_results命令不再通过wpa_supplicant下发到kernel,而是直接由wificond传送到kernel,而scan results的结果也是直接由kernel传给wificond,再由wificond传送给上层去显示;

          (3) wifi direct的p2p find还是通过wpa-supplicant通信 ;

          (4) wifi的连接等操作,还是通过wpa_supplicant进行。

    展开全文
  • 查找资料发现,原来驱动都是正确安装了的,但是笔记本需要按Fn+F2开启无线wifi连接。命令行终端输入:lshw -C network*-networkdescription: Ethernet interfaceproduct: RTL8101E/RTL8102E PCI Express Fa...
  • 杂谈:WiFi7-802.11be

    2020-10-28 17:44:41
    命名: 8011be wifi7 EHT(Extremely High Throughput) 目标: 30Gbps, 16 Nss 应用场景: 4k streaming and online gaming. Technology: 320MHz bandwidth 4096 QAM More efficient use of noncontiguous ...
  • Measuring Audio Latency

    2016-04-19 12:17:00
    Measuring Audio Latency This page describes common methods for measuring input and output latency. Measuring Output Latency There are several techniques available to measure output latency, with ...
  • Android Audio延迟(latency)

    万次阅读 2012-06-06 10:19:25
    最近在看Android中播放延迟的问题,看了下代码,发现AudioTrack类中的函数latency有以下注释: /* Returns this track's latency in milliseconds. * This includes the latency due to AudioTrack buffer size, ...
  • Ubuntu18.04 wifi不稳定

    千次阅读 2018-10-02 20:41:07
    在ThinkPad X201i用ubuntu16.04已经很久了, wifi连接一直不稳定, 用几分钟就自动断开, 无奈只能重连. 查询无线网卡具体情况如下: 查看wifi连接情况 $ lspci 02:00.0 Network controller: Intel Corporation Centrino...
  • WiFi 扫描处理过程

    2020-11-20 17:52:44
    * See {@link android.net.wifi.WifiManager#startScan} * * @param packageName Package name of the app that requests wifi scan. * @param featureId The feature in the package */ @Ov
  • 64 bits clock: 33MHz capabilities: bus_master cap_list configuration: latency=0 *-network description: Ethernet interface product: RTL8101E PCI Express Fast Ethernet controller vendor: Realtek ...
  • ubuntu16.04 无法wifi链接一段时间掉线且无法再连接,从网上搜索的确认这个一个bug。解决方法:1.Get details of your PCI wireless card by running sudo lshw -class network显示信息如下:*-networkdescription: ...
  • [root@localhost DPO_MT7601U_LinuxSTA_3.0.0.4_...1 pci 0000:00:01.0: PCI INT A -> GSI 16 (level, low) -> IRQ 16 pci 0000:00:01.0: setting latency timer to 64 pci 0000:00:1c.0: PCI INT A -> GSI 16 (level,...
  • WiFi基本参数

    千次阅读 2020-08-28 09:41:58
    分享几个WiFi的几个核心参数 Beacon Interval 间隔调高有助于无线网络效能 client 端省电, 间隔调低可以加快wireless client 链接上去速度 Beacon传送频繁frame会比较占用无线带宽资源 Beacon 单位通常以微秒 ...
  •  // APB2时钟:72MHz HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2); // HAL_RCC_GetHCLKFreq()/1000 1ms中断一次 // HAL_RCC_GetHCLKFreq()/100000 10us中断一次 // HAL_RCC_GetHCLKFreq()/1000000...
  • 使用命令行连接 wifi

    千次阅读 2018-12-17 20:30:51
    使用命令行连接 wifi 以前尝试过通过命令行连接 wifi,最后没有成功,已经是几年前的印象。继续尝试,我发 现 iwconfig 不支持秘钥,只能用于连接开放的热点。 下面是我在使用命令行连接 wifi 的时候遇到的问题及...
  •  这次接着讲Wifi工程流程中的Wifi热点查找过程,也是Wifi启动的过程延续,Wifi启动过程中会更新Wifi的状态,框架层也有相应广播发出,应用层接收到广播后开始进行热点的扫描。可以先看前面Wifi启动的分析过程。 ...
  • 没有wifi的原因: r9000p用的联发科relatek MT7921 WIFI6的网卡,系统内核没有兼容的驱动. 查看网络:rfkill list all 没有WIFI时 ~$ rfkill list all 0: ideapad_wlan: Wireless LAN Soft blocked: no Har.
  • /********************************************************************************* 文件名程: main.c* 作 者: 硬石嵌入式开发团队* 版 本: V1.0* 编写日期: 2015-10-04* 功 能: WiFi(ESP8266)底层驱动实现*****...
  • WiFi learn EDCA

    2018-01-18 22:08:09
    There are plans to incorporate quality of service (QoS) capabilities in WiFi technology with the adoption of the IEEE 802.11e standard. The 802.11e standard will include two operating modes, ...
  • 网卡驱动的问题!!! 在终端输入lspci -v 往下翻 ...$ lspci -v … 02:00.0 Ethernet controller: Realtek Semiconductor ...点右上角WiFi图标就可以看见能搜索到WiFi了。 所以我为什么要装Deepin呜呜呜
  • 新电脑安装了Ubuntu 14.04,但是网络连接中只有以太网而没有WiFi的选项. 打开System Setting系统设置-Software&amp;Updates软件&amp;更新-Additional Drivers附加驱动,无法加载出相应的驱动,更换为国内源也...
  • Ubuntu下WIFI不稳定问题

    2021-08-21 17:54:24
    Ubuntu下无线网络总是不稳定,显示WIFI开着,密码也正确,但是连接时却一直连不上,显示一直connecting,建立WIFI连接时可以看到有如下syslog: Aug 15 20:00:25 ubuntu wpa_supplicant[1071]: wlp3s0: ...
  • SDR WiFi平台 gr-ieee802-11 软件无线电实现802.11协议

    万次阅读 热门讨论 2016-12-24 15:52:55
    SDR WiFi平台 gr-ieee802-11 软件无线电实现802.11协议。这篇博客主要介绍SDR WiFi平台gr-ieee802-11。据笔者所知,可能因为802.11协议要求的带宽较大,数据速率较大,对SDR平台的性能要求较高,所以较少实现SDR ...
  • 神舟电脑Ubuntu无WIFI

    2018-12-12 11:49:05
    1、新电脑安装了Ubuntu 14.04,但是网络连接中只有以太网而没有WiFi的选项. 2、打开System Setting系统设置-Software&amp;Updates软件&amp;更新-Additional Drivers附加驱动,无法加载出相应的...
  • Linux WIFI模块驱动移植

    千次阅读 2020-10-27 22:22:55
    无线网卡驱动Qualcomm Atheros QCA9565 / AR9565 Wireless Network Adapter imx6ull linux下wifi驱动移植 Linux移植wifi驱动 嵌入式系统添加无线wifi模块 基于Atheros 300M MiniPCI网卡(AR9223-AL1A)的嵌入式x86...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,213
精华内容 885
关键字:

latencywifi