android framework base usb server 分析

本文深入探讨了Android系统中USB框架的各个组成部分,包括关键的Java类和服务,如UsbDeviceManager、UsbManager以及UsbDebuggingManager等,并详细解释了它们的功能与交互方式。
Android 下的usb主要工作还是在android的framework层。主要有以下几个文件:

1.1UsbDeviceManager.java/高

主要完成功能切换及状态的更新,需要向外广播状态,接收kernel发送过来的event信息。实质性的工作都在这里边完成。

重要等级:高

1.2 UsbManager.java /usbService.java/中

这两个主要是接口,因为usb的工作本身就应该在kernel里,framework只是简单的逻辑流程重组。Android
framework中****Manager.java是给用户提供的接口。

下面这个体现了manager、service的工作方式,用户会通过usbManager的调用来完成usb功能的设置。

public int setUsbTethering(boolean enable){

UsbManager usbManager =(UsbManager)mContext.getSystemService(Context.USB_SERVICE);

………………

else {

mUsbTetherRequested = true;

usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS,false);

}

1.3UsbSettings.java

packages/apps/Settings/src/com/android/settings/deviceinfo

这个是用户在setting中,选择不同的存储类型时的,进行的界面更新及状态切换的处理流程。

1.4UsbSettingsManager.java

frameworks/base/services/java/com/android/server/usb

没有明白具体功能是干什么的,在UsbDeviceManager.java有用到一些。

发送usb device attached

1.5StorageNotification.java/高

frameworks/base/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java

主要用来处理存储设备相关动作的状态更新、通知。主要涉及两个方面:一是插入usbcabel后的状态栏更新,主要针对ums功能。二是storage状态发生改变时,会调用这个activity,如mount、unmount等。

所以入口主要有两个:onUsbMassStorageConnectionChanged和onStorageStateChanged分别处理usb连接状态的改变和storage状态的改变。

1.6UsbStorageActivity.java/高

启动u盘功能后的一些ui的处理,状态的监控等处理,做的工作不多。

1.7Usbstring.c

kernel/driver/usb/gadget/ 用来进行字符串格式的转换

1.8UsbDevice.java

/frameworks/base/core/java/android/hardware/usb

主要是usb设备的一些接口,基本上是获取的设备描述符。

1.9UsbDeviceConnection.java

/frameworks/base/core/java/android/hardware/usb

usb设备的打开,基本传输等动作,似乎是native层面的。

1.10UsbDisconnectedReceiver.java

/frameworks/base/core/java/android/hardware/usb

接收断开的信息,android.hardware.usb.action.USB_DEVICE_DETACHED

1.11UsbHostManager.java

处理host情况下的事件流程:

UsbDeviceAdded函数:报告有USB设备插入,通过getCurrentSettings().deviceAttached发送intent消息:UsbManager.ACTION_USB_DEVICE_ATTACHED。

对应的JNI函数com_android_server_UsbHostManager.cpp

其中的monitorUsbHostBus会调用usb_host_run函数(system/core/libusbhost/usbhost.c)不停的读取bus总线,读取到以后,调用注册的回调函数usb_device_added--àUsbDeviceAdded(method_usbDeviceAdded)




1. 首先在system server 中有以下代码

boolean disableNonCoreServices = SystemProperties.getBoolean("config.disable_noncore", false);

            if (!disableNonCoreServices) {
                try {
                    Slog.i(TAG, "USB Service");
                    // Manage USB host and device support  , usb 服务的启动
                    usb = new UsbService(context);
                    ServiceManager.addService(Context.USB_SERVICE, usb);
                } catch (Throwable e) {
                    reportWtf("starting UsbService", e);
                }

                try {
                    Slog.i(TAG, "Serial Service");
                    // Serial port support  串口服务的启动
                    serial = new SerialService(context);
                    ServiceManager.addService(Context.SERIAL_SERVICE, serial);
                } catch (Throwable e) {
                    Slog.e(TAG, "Failure starting SerialService", e);
                }
            }

2.  看 usbservice 的实现

public class UsbService extends IUsbManager.Stub

    public UsbService(Context context) {
        mContext = context;

        final PackageManager pm = mContext.getPackageManager();
        if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
            mHostManager = new UsbHostManager(context);
        }
        if (new File("/sys/class/android_usb").exists()) {
            mDeviceManager = new UsbDeviceManager(context);
        }

        setCurrentUser(UserHandle.USER_OWNER);

        final IntentFilter userFilter = new IntentFilter();
        userFilter.addAction(Intent.ACTION_USER_SWITCHED);
        userFilter.addAction(Intent.ACTION_USER_STOPPED);
        mContext.registerReceiver(mUserReceiver, userFilter, null, null);
    }


    public UsbHostManager(Context context) {
        mContext = context;
        mHostBlacklist = context.getResources().getStringArray(
                com.android.internal.R.array.config_usbHostBlacklist);
    }


    public UsbDeviceManager(Context context) {
        mContext = context;
        mContentResolver = context.getContentResolver();
        PackageManager pm = mContext.getPackageManager();
        mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
        initRndisAddress();

        readOemUsbOverrideConfig();

        mHandler = new UsbHandler(FgThread.get().getLooper());

        if (nativeIsStartRequested()) {
            if (DEBUG) Slog.d(TAG, "accessory attached at boot");
            startAccessoryMode();
        }

        boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
        boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt"));
        if (secureAdbEnabled && !dataEncrypted) {
            mDebuggingManager = new UsbDebuggingManager(context);
        }
    }



/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions an
 * limitations under the License.
 */

package com.android.server.usb;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Handler;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.util.Slog;
import android.util.Base64;
import com.android.server.FgThread;

import java.lang.Thread;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.security.MessageDigest;
import java.util.Arrays;

public class UsbDebuggingManager implements Runnable {
    private static final String TAG = "UsbDebuggingManager";
    private static final boolean DEBUG = false;

    private final String ADBD_SOCKET = "adbd";
    private final String ADB_DIRECTORY = "misc/adb";
    private final String ADB_KEYS_FILE = "adb_keys";
    private final int BUFFER_SIZE = 4096;

    private final Context mContext;
    private final Handler mHandler;
    private Thread mThread;
    private boolean mAdbEnabled = false;
    private String mFingerprints;
    private LocalSocket mSocket = null;
    private OutputStream mOutputStream = null;

    public UsbDebuggingManager(Context context) {
        mHandler = new UsbDebuggingHandler(FgThread.get().getLooper());
        mContext = context;
    }

    private void listenToSocket() throws IOException {
        try {
            byte[] buffer = new byte[BUFFER_SIZE];
            LocalSocketAddress address = new LocalSocketAddress(ADBD_SOCKET,
                                         LocalSocketAddress.Namespace.RESERVED);
            InputStream inputStream = null;

            mSocket = new LocalSocket();
            mSocket.connect(address);

            mOutputStream = mSocket.getOutputStream();
            inputStream = mSocket.getInputStream();

            while (true) {
                int count = inputStream.read(buffer);
                if (count < 0) {
                    Slog.e(TAG, "got " + count + " reading");
                    break;
                }

                if (buffer[0] == 'P' && buffer[1] == 'K') {
                    String key = new String(Arrays.copyOfRange(buffer, 2, count));
                    Slog.d(TAG, "Received public key: " + key);
                    Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_CONFIRM);
                    msg.obj = key;
                    mHandler.sendMessage(msg);
                }
                else {
                    Slog.e(TAG, "Wrong message: " + (new String(Arrays.copyOfRange(buffer, 0, 2))));
                    break;
                }
            }
        } catch (IOException ex) {
            Slog.e(TAG, "Communication error: ", ex);
            throw ex;
        } finally {
            closeSocket();
        }
    }

    @Override
    public void run() {
        while (mAdbEnabled) {
            try {
                listenToSocket();
            } catch (Exception e) {
                /* Don't loop too fast if adbd dies, before init restarts it */
                SystemClock.sleep(1000);
            }
        }
    }

    private void closeSocket() {
        if (mOutputStream != null) {
            try {
                mOutputStream.close();
            } catch (IOException e) {
                Slog.e(TAG, "Failed closing output stream: " + e);
            }
        }

        if (mSocket != null) {
            try {
                mSocket.close();
            } catch (IOException ex) {
                Slog.e(TAG, "Failed closing socket: " + ex);
            }
        }
    }

    private void sendResponse(String msg) {
        if (mOutputStream != null) {
            try {
                mOutputStream.write(msg.getBytes());
            }
            catch (IOException ex) {
                Slog.e(TAG, "Failed to write response:", ex);
            }
        }
    }

    class UsbDebuggingHandler extends Handler {
        private static final int MESSAGE_ADB_ENABLED = 1;
        private static final int MESSAGE_ADB_DISABLED = 2;
        private static final int MESSAGE_ADB_ALLOW = 3;
        private static final int MESSAGE_ADB_DENY = 4;
        private static final int MESSAGE_ADB_CONFIRM = 5;
        private static final int MESSAGE_ADB_CLEAR = 6;

        public UsbDebuggingHandler(Looper looper) {
            super(looper);
        }

        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_ADB_ENABLED:
                    if (mAdbEnabled)
                        break;

                    mAdbEnabled = true;

                    mThread = new Thread(UsbDebuggingManager.this, TAG);
                    mThread.start();

                    break;

                case MESSAGE_ADB_DISABLED:
                    if (!mAdbEnabled)
                        break;

                    mAdbEnabled = false;
                    closeSocket();

                    try {
                        mThread.join();
                    } catch (Exception ex) {
                    }

                    mThread = null;
                    mOutputStream = null;
                    mSocket = null;
                    break;

                case MESSAGE_ADB_ALLOW: {
                    String key = (String)msg.obj;
                    String fingerprints = getFingerprints(key);

                    if (!fingerprints.equals(mFingerprints)) {
                        Slog.e(TAG, "Fingerprints do not match. Got "
                                + fingerprints + ", expected " + mFingerprints);
                        break;
                    }

                    if (msg.arg1 == 1) {
                        writeKey(key);
                    }

                    sendResponse("OK");
                    break;
                }

                case MESSAGE_ADB_DENY:
                    sendResponse("NO");
                    break;

                case MESSAGE_ADB_CONFIRM: {
                    String key = (String)msg.obj;
                    mFingerprints = getFingerprints(key);
                    showConfirmationDialog(key, mFingerprints);
                    break;
                }

                case MESSAGE_ADB_CLEAR:
                    deleteKeyFile();
                    break;
            }
        }
    }

    private String getFingerprints(String key) {
        String hex = "0123456789ABCDEF";
        StringBuilder sb = new StringBuilder();
        MessageDigest digester;

        try {
            digester = MessageDigest.getInstance("MD5");
        } catch (Exception ex) {
            Slog.e(TAG, "Error getting digester: " + ex);
            return "";
        }

        byte[] base64_data = key.split("\\s+")[0].getBytes();
        byte[] digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT));

        for (int i = 0; i < digest.length; i++) {
            sb.append(hex.charAt((digest[i] >> 4) & 0xf));
            sb.append(hex.charAt(digest[i] & 0xf));
            if (i < digest.length - 1)
                sb.append(":");
        }
        return sb.toString();
    }

    private void showConfirmationDialog(String key, String fingerprints) {
        Intent dialogIntent = new Intent();

        dialogIntent.setClassName("com.android.systemui",
                "com.android.systemui.usb.UsbDebuggingActivity");
        dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        dialogIntent.putExtra("key", key);
        dialogIntent.putExtra("fingerprints", fingerprints);
        try {
            mContext.startActivity(dialogIntent);
        } catch (ActivityNotFoundException e) {
            Slog.e(TAG, "unable to start UsbDebuggingActivity");
        }
    }

    private File getUserKeyFile() {
        File dataDir = Environment.getDataDirectory();
        File adbDir = new File(dataDir, ADB_DIRECTORY);

        if (!adbDir.exists()) {
            Slog.e(TAG, "ADB data directory does not exist");
            return null;
        }

        return new File(adbDir, ADB_KEYS_FILE);
    }

    private void writeKey(String key) {
        try {
            File keyFile = getUserKeyFile();

            if (keyFile == null) {
                return;
            }

            if (!keyFile.exists()) {
                keyFile.createNewFile();
                FileUtils.setPermissions(keyFile.toString(),
                    FileUtils.S_IRUSR | FileUtils.S_IWUSR |
                    FileUtils.S_IRGRP, -1, -1);
            }

            FileOutputStream fo = new FileOutputStream(keyFile, true);
            fo.write(key.getBytes());
            fo.write('\n');
            fo.close();
        }
        catch (IOException ex) {
            Slog.e(TAG, "Error writing key:" + ex);
        }
    }

    private void deleteKeyFile() {
        File keyFile = getUserKeyFile();
        if (keyFile != null) {
            keyFile.delete();
        }
    }

    public void setAdbEnabled(boolean enabled) {
        mHandler.sendEmptyMessage(enabled ? UsbDebuggingHandler.MESSAGE_ADB_ENABLED
                                          : UsbDebuggingHandler.MESSAGE_ADB_DISABLED);
    }

    public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
        Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_ALLOW);
        msg.arg1 = alwaysAllow ? 1 : 0;
        msg.obj = publicKey;
        mHandler.sendMessage(msg);
    }

    public void denyUsbDebugging() {
        mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_DENY);
    }

    public void clearUsbDebuggingKeys() {
        mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_CLEAR);
    }

    public void dump(FileDescriptor fd, PrintWriter pw) {
        pw.println("  USB Debugging State:");
        pw.println("    Connected to adbd: " + (mOutputStream != null));
        pw.println("    Last key received: " + mFingerprints);
        pw.println("    User keys:");
        try {
            pw.println(FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
        } catch (IOException e) {
            pw.println("IOException: " + e);
        }
        pw.println("    System keys:");
        try {
            pw.println(FileUtils.readTextFile(new File("/adb_keys"), 0, null));
        } catch (IOException e) {
            pw.println("IOException: " + e);
        }
    }
}



<think>我们正在讨论如何在Android系统中调整USB设备的枚举优先级,以解决多个USB设备同时接入时的冲突问题(如RK3588Android11上同时接入USB麦克风和USB摄像头导致麦克风无法使用)。根据之前的讨论,我们知道设备枚举顺序可能影响设备的功能正常性。在Android系统中,USB设备的枚举顺序主要由Linux内核控制,但我们可以通过一些系统级配置来影响枚举的优先级。以下是一些可行的方法:###1. **修改USB设备初始化延迟(通过内核参数)**在Linux内核中,可以通过为不同的USB设备设置不同的初始化延迟(autosuspenddelay)来间接控制枚举顺序。在设备树(DeviceTree)或内核启动参数中,我们可以为特定的USB端口设置不同的初始化时间。例如,在设备树中为USB控制器节点添加属性:```dts&usb0 {compatible= "generic-xhci";status ="okay";/*设置端口1(摄像头)的延迟为1000ms,端口2(麦克风)的延迟为2000ms */port@1 {reg= <1>;autosuspend-delay= <1000>;};port@2 {reg= <2>;autosuspend-delay= <2000>;};};```这样,连接在端口1的设备(摄像头)会比端口2的设备(麦克风)更早被初始化。注意:这需要内核支持,并且具体的设备树节点路径需要根据硬件平台调整。 ###2. **通过udev规则调整(如果Android系统支持)**标准的Linux系统通常使用udev规则来管理设备,但Android系统通常不使用udev。然而,如果系统支持(例如在较新的Android版本或定制ROM中),可以尝试添加udev规则。在`/etc/udev/rules.d/`目录下创建规则文件(如`10-usb-priority.rules`):```bash#给USB摄像头(通过厂商ID和产品ID识别)设置更高的优先级SUBSYSTEM=="usb", ATTR{idVendor}=="abcd", ATTR{idProduct}=="1234",ENV{ID_PRIORITY}="100"#给USB麦克风设置较低的优先级SUBSYSTEM=="usb", ATTR{idVendor}=="5678",ATTR{idProduct}=="efgh",ENV{ID_PRIORITY}="50"```然后重启udev服务。但请注意,Android系统通常不包含完整的udev,因此这种方法可能不适用。 ###3. **在Android框架层调整USB设备加载顺序**在Android系统中,`UsbManager`负责管理USB设备[^1]。我们可以修改`UsbService`的初始化逻辑,在检测到多个USB设备时,按照我们设定的顺序进行初始化。修改`UsbService.java`(位于`frameworks/base/services/usb/java/com/android/server/usb/`):```javapublic voidsystemReady(){...//获取连接的USB设备列表HashMap<String,UsbDevice>devices= mUsbHostManager.getDeviceList();//按照设备类型进行排序:先初始化摄像头,再初始化麦克风List<UsbDevice>sortedDevices =new ArrayList<>(devices.values());Collections.sort(sortedDevices,new Comparator<UsbDevice>() {@Overridepublicint compare(UsbDevicedev1, UsbDevicedev2) {//返回负数表示dev1排在dev2前面if(isCamera(dev1)&& !isCamera(dev2)){return -1;}else if(!isCamera(dev1) &&isCamera(dev2)){return1;}else if(isMicrophone(dev1)&& !isMicrophone(dev2)) {return1; //麦克风后初始化}return0;}});//按照排序后的顺序初始化设备for (UsbDevice device: sortedDevices) {mUsbHostManager.usbDeviceAdded(device);}...}```注意:此修改需要重新编译系统框架(framework),并且需要根据设备的具体类型判断(通过设备类、子类、协议等)。###4.**调整USB控制器带宽分配**根据引用[4],USB2.0支持不同的速率(低速1.5Mbps、全速12Mbps、高速480Mbps)。我们可以通过限制高带宽设备(如摄像头)的速率来避免冲突。在内核中,可以通过修改USB主机控制器驱动(xhci)来限制特定设备的带宽:```c//在drivers/usb/host/xhci.c中,修改带宽分配策略static intxhci_check_bandwidth(structusb_hcd*hcd,struct usb_device*udev){...//如果是摄像头设备,限制其带宽为全速(12Mbps)if(is_camera_device(udev)) {udev->speed= USB_SPEED_FULL;}...}```这样,摄像头将使用较少的带宽,从而为麦克风留出足够的资源。 ###5. **使用USBHUB端口顺序控制**物理上,USB HUB的端口顺序会影响枚举顺序。将需要优先枚举的设备(如摄像头)连接到HUB的第一个端口(通常标记为1),将麦克风连接到第二个端口(标记为2)。这样,摄像头会先被枚举。 ###验证方法1.查看内核日志:`adbshell dmesg| grep-iusb`2.检查USB设备列表:`adbshell lsusb`(如果系统支持)3.使用`adb shelldumpsys usb`查看设备加载顺序###注意事项-修改内核或框架需要重新编译系统,并且需要设备的root权限或系统签名。-不同硬件平台(如RK3588)的USB控制器实现可能不同,需参考具体的内核源码。-在Android11上,由于权限限制,应用层无法直接控制枚举顺序,因此系统级修改是必要的。 §§相关问题§§1.如何通过Android的`UsbManager`类动态控制USB设备的访问权限?2.在Linux内核中,如何为不同的USB设备设置不同的电源管理参数? 3.如何通过USB设备描述符(如设备类、子类)来识别特定类型的USB设备? 4. Android系统如何为USB音频设备加载特定的音频策略?5.在RK3588平台上,如何查看USB控制器的带宽使用情况?</think>在 Android 系统中调整 USB 设备的枚举优先级,可通过以下系统级方法解决设备冲突问题(如 RK3588 Android 11 的 USB 麦克风与摄像头冲突): ### 一、内核级枚举优先级调整 1. **修改 USB 主机控制器驱动** 在 Linux 内核中编辑 `drivers/usb/core/generic.c`,添加设备优先级表: ```c static struct usb_device_id priority_table[] = { { USB_DEVICE(0x0d8c, 0x0012) }, // USB 麦克风 VID:PID { USB_DEVICE(0x046d, 0x082d) }, // USB 摄像头 VID:PID {} // 终止项 }; static int usb_enumeration_priority(struct usb_device *udev) { if (usb_match_id(udev, priority_table)) return 1; // 高优先级 return 0; // 默认优先级 } ``` 在枚举函数 `usb_new_device()` 中调用此逻辑[^1][^3]。 2. **USB 端口延迟初始化** 在设备树(DTS)中为特定端口设置延迟: ```dts &usb0 { port@1 { reg = <1>; autosuspend-delay-ms = <2000>; // 摄像头端口延迟 2s }; port@2 { reg = <2>; autosuspend-delay-ms = <500>; // 麦克风端口优先初始化 }; }; ``` ### 二、Android 框架层控制 1. **修改 UsbService 逻辑** 在 `frameworks/base/services/usb/java/com/android/server/usb/UsbService.java` 中: ```java private void handleDeviceAdded(UsbDevice device) { // 优先处理音频类设备 (0x01) if (device.getDeviceClass() == UsbConstants.USB_CLASS_AUDIO) { mHandler.sendMessageDelayed( Message.obtain(mHandler, MSG_DEVICE_ADDED, device), 100); // 高优先级 } else { mHandler.sendMessageDelayed( Message.obtain(mHandler, MSG_DEVICE_ADDED, device), 2000); // 低优先级延迟 } } ``` 2. **USB 设备过滤器配置** 在 `device/<manufacturer>/<device>/init.rc` 中添加: ```rc on boot # 设置 USB 设备初始化顺序 setprop sys.usb.init.priority "1-1.1:audio,1-1.2:video" start usb_priority_service ``` ### 三、动态优先级调整接口 通过 `UsbManager` 实现应用层控制[^1]: ```java // 在应用内设置设备优先级 UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); HashMap<String, UsbDevice> devices = manager.getDeviceList(); // 识别并优先请求麦克风权限 for (UsbDevice device : devices.values()) { if (device.getDeviceClass() == UsbConstants.USB_CLASS_AUDIO) { PendingIntent permissionIntent = ...; manager.requestPermission(device, permissionIntent); // 优先请求权限 break; } } ``` ### 四、验证与调试 1. **查看枚举顺序**: ```bash adb shell dmesg | grep "usb new device" # 输出示例:usb 1-1.1: new high-speed USB device ... # 麦克风 # usb 1-1.2: new high-speed USB device ... # 摄像头 ``` 2. **检查设备节点**: ```bash adb shell ls /dev/bus/usb/001/ # 优先初始化的设备会获得更小的设备号(如001/002) ``` ### 五、带宽优化补充 若摄像头占用过多带宽(引用[4]): 1. **强制降速**(在摄像头驱动中): ```c // 修改 drivers/media/usb/uvc/uvc_driver.c static int uvc_probe(struct usb_interface *intf...) { if (dev->width > 1280) // 限制高清摄像头 udev->speed = USB_SPEED_FULL; // 强制全速(12Mbps) } ``` > **关键点**: > - 枚举优先级由内核到应用层逐级传递 > - 音频设备(0x01类)应优先于视频设备(0x0E类) > - 通过延迟控制可解决硬件初始化竞争问题[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值