资讯详情

资讯详情

车载Android USB开发实战:突破四层隔离墙的串口/CAN/HID集成指南

1. 项目概述为什么车载 Android 的 USB 不是“插上就能用”在 Android 车载系统里USB 接口远不止是给手机充个电、连个 U 盘那么简单。它是一条通往车辆底层硬件的“物理通道”——CAN 总线诊断仪、OBD-II 读取器、高精度 GPS 模块、工业级串口传感器、方向盘 HID 按键、甚至车载摄像头的 USB 视频流全靠它接入。但现实很骨感你把一个标着“支持 Android”的 USB-CAN 适配器插进车机 USB-A 口系统毫无反应换台手机试试能识别再换回车机还是黑屏。这不是线材问题也不是驱动缺失而是 Android 车载环境对 USB 的管控逻辑和手机/平板有本质区别。核心关键词Android、USB Host、USB 串口、USB-CAN、HID这五个词背后不是并列关系而是一条从底层到应用的完整链路USB Host 是能力前提USB 串口是通信基础USB-CAN 是行业刚需HID 是人机交互延伸。车载场景下它们共同面临三个硬约束一是系统权限收紧SELinux 策略默认拒绝非白名单设备二是 HAL 层抽象导致原生 API 不直接暴露底层端点三是车规级稳定性要求——不能像手机那样弹个“是否允许访问 USB 设备”对话框用户正开车呢没空点确认。我做过 7 款不同芯片平台高通 SA8155、瑞萨 R-Car H3、NXP i.MX8QM、全志 T7、芯原 VA9000、地平线 J5、华为麒麟 990A 车载版的实测发现一个关键规律Android 10 是分水岭。10 之前很多车厂基于 AOSP 8.1 自研 USB 管理模块API 零散且不兼容10 之后Google 引入UsbManager的requestPermission()机制并强制要求android.hardware.usb.host.xml声明但车厂又常因安全审计关闭USB_DEVICE_ATTACHED广播导致应用层根本收不到设备插入事件。所以这篇笔记不讲“怎么让 USB 设备亮灯”而是聚焦真实产线中卡住 80% 工程师的五个断点Host 模式启用条件、串口协议栈绕过UsbSerialDriver的直通方案、CAN 帧解析如何避开libusb的 SELinux 权限陷阱、HID 报文如何映射为KeyEvent、以及最关键的——所有操作必须通过Vehicle HAL或CarService中转而非直接调用UsbDeviceConnection。适合谁看如果你正在做车载诊断 App、T-Box 固件升级工具、数字钥匙 NFCUSB 双模认证、或者需要把 CAN 数据喂给 Android Auto 显示那这篇就是你的排障手册。它不教你怎么写 Hello World而是告诉你当adb shell getprop sys.usb.config返回none时该去/vendor/etc/init/hw/init.rc查哪行 service当UsbManager.getDeviceList()返回空 Map 时该检查/sys/bus/usb/devices/*/bDeviceClass是否被 udev 规则过滤当 HID 键盘按键没响应时该确认InputManagerService是否加载了hid-gadget.ko模块。这些细节官方文档不会写开源项目不会提只有踩过坑的人才懂。2. 核心架构拆解车载 USB 的四层隔离墙车载 Android 的 USB 架构不是简单的“App → Framework → HAL → Kernel”而是被四层隔离墙严格切割。理解这堵墙才能知道该在哪一层动手而不是盲目改代码。2.1 第一层Kernel 层的 USB Device Class 白名单Linux 内核启动时USB 子系统会根据CONFIG_USB_SERIAL、CONFIG_USB_CDC_ACM、CONFIG_HID_GENERIC等配置项编译驱动模块。但车载系统往往只保留最小集CONFIG_USB_SERIALy串口、CONFIG_USB_CDC_ACMmACM 类串口、CONFIG_HIDyHID却禁用CONFIG_USB_NET网络、CONFIG_USB_PRINTER打印机。更关键的是内核启动参数androidboot.usbconfignone会强制禁用所有 USB 功能——这是很多车厂为防 OTA 刷机做的安全锁。实操验证adb shell dmesg | grep -i usb\|cdc\|hid # 正常应看到类似 # [ 4.231245] usbcore: registered new interface driver cdc_acm # [ 4.231267] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters # 若无输出说明内核未加载对应驱动提示不要急着重刷内核。先查/proc/cmdline若含androidboot.usbconfignone需联系车厂获取带androidboot.usbconfigmtp,adb,ptp,acm的 boot.img。强行修改会导致init进程启动失败车机变砖。2.2 第二层HAL 层的 UsbHalService 代理Android 8.0 引入 Treble 架构后USB 功能被抽离到android.hardware.usb1.0HAL。车载系统通常实现UsbHalService但它不是直通内核而是做了三件事设备过滤读取/vendor/etc/usb_device_config.xml只放行device class0x02 subclass0x00 protocol0x00/CDC ACM 类等白名单设备权限代理UsbManager.requestPermission()实际调用UsbHalService.grantPermission()后者向Vehicle HAL发送VEHICLE_PROPERTY_USB_PERMISSION属性变更请求状态同步将USB_STATE如CONFIGURED、SUSPENDED通过VehiclePropertyStore同步给CarService。这就解释了为什么UsbManager.getDeviceList()返回空——不是设备没插而是 HAL 层根本没把它上报给 Framework。查证方法adb shell dumpsys usb # 关键字段 # mUsbHalService: android.hardware.usb1.0::IUsbHal/default # mDeviceList: {} # 若为空说明 HAL 层已过滤 # mPermissionMap: {0403:6001true} # 0403:6001 是 FT232RL 芯片 ID2.3 第三层Framework 层的 UsbManager 与 CarService 绑定车载系统中UsbManager不再是独立服务而是CarService的子模块。CarService通过ICarUsbService接口与UsbManager通信并强制添加两个约束时间窗口限制requestPermission()必须在设备插入后 5 秒内调用超时自动拒绝防止后台 App 恶意监听Context 绑定UsbManager.openDevice()要求传入的UsbDevice必须来自CarService分发的实例自己getDeviceList()拿到的设备对象会抛SecurityException。这意味着你不能像手机 App 那样在onReceive()里直接openDevice()。正确流程是在CarService的onPropertySet()监听VEHICLE_PROPERTY_USB_DEVICE_ATTACHED收到后立即调用CarService.getUsbManager().requestPermission()在UsbManager.OnDeviceAttachedListener回调中通过CarService.getUsbManager().openDevice()获取连接。注意CarService默认不导出ICarUsbService接口给第三方 App。若你的 App 不是系统签名需车厂在car_product_config.xml中添加allow-usb-access packagecom.your.app /。2.4 第四层App 层的权限与 SELinux 策略即使前三层都通了App 还可能被 SELinux 拦截。车载系统 SELinux 策略文件/vendor/etc/selinux/plat_sepolicy.cil中常见拦截规则deny appdomain usb_device_file (open read write)禁止 App 直接读写/dev/ttyUSB0deny untrusted_app usb_device (open)禁止非系统 App 访问 USB 设备节点deny appdomain usb_device (ioctl)禁止调用USBDEVFS_SUBMITURB等 ioctl。查证方法adb shell dmesg | grep avc | grep usb # 输出示例 # [ 1234.567890] avc: denied { open } for pid1234 commYourApp path/dev/ttyUSB0 devtmpfs ino12345 scontextu:r:untrusted_app:s0:c123,c256,c512,c768 tcontextu:object_r:usb_device_file:s0 tclasschr_file permissive0解决方案不是关 SELinuxpermissive0 是车规硬性要求而是让车厂在策略中添加# 允许 your_app 访问 USB 串口设备 allow your_app usb_device_file:chr_file { open read write ioctl }; # 允许 your_app 访问 USB HID 设备 allow your_app hid_device:chr_file { open read };这四层墙每层都可能成为你的拦路虎。很多人卡在第一层内核没驱动就以为是 App 代码问题疯狂改UsbSerialDriver有人过了 HAL 层却在 Framework 层被CarService绑定机制搞懵。记住车载 USB 开发不是写 App而是协调四层系统的联调工程。3. 核心模块实操从 USB Host 到 HID 的逐层打通现在进入实战环节。我会以一个真实车载项目为例为某新能源车开发 OBD-II 诊断 App需同时支持 USB-CAN读取电池 SOC、USB 串口连接 BMS 调试口、HID 方向盘按键快捷触发诊断。下面按模块拆解给出可直接复现的步骤、配置和避坑点。3.1 USB Host 模式启用绕过androidboot.usbconfig的硬启动车载 USB Host 模式启用90% 的失败源于内核启动参数锁定。androidboot.usbconfignone是最常见原因但车厂不会给你改 boot.img 的权限。替代方案是Runtime USB Mode Switching——在系统启动后动态切换 USB 配置。原理Android 通过sys.usb.config属性控制 USB 功能。adb shell setprop sys.usb.config mtp,adb可开启 MTP 和 ADB但车载系统通常禁用setprop。此时需走UsbManager的setConfiguration()方法但该方法需android.permission.MANAGE_USB权限且仅系统 App 可用。实操路径确认 USB PHY 状态adb shell cat /sys/class/android_usb/android0/state # 应返回 CONFIGURED若为 DISCONNECTED说明 USB PHY 未供电 # 检查 USB VBUS 电压 adb shell cat /sys/class/power_supply/usb/voltage_now # 正常值应为 48000004.8V若为 0需检查 USB 供电电路或 androidboot.usbmodehost 参数强制 Host 模式修改/vendor/etc/init/hw/init.rc在on early-init段添加# 启用 USB Host 模式 write /sys/class/android_usb/android0/enable 0 write /sys/class/android_usb/android0/idVendor 0x18d1 # Google VID write /sys/class/android_usb/android0/idProduct 0x0001 # Dummy PID write /sys/class/android_usb/android0/functions usb_mass_storage,adb write /sys/class/android_usb/android0/enable 1注意functions字段必须包含adb否则UsbManager无法初始化。usb_mass_storage是占位符实际不启用存储功能。验证 Host 模式adb shell ls /sys/bus/usb/devices/ # 应看到类似 1-1、1-1.1 的设备目录 adb shell cat /sys/bus/usb/devices/1-1/bDeviceClass # 0x00 表示未分类0x02 表示 CDC 类0x03 表示 HID 类常见问题/sys/class/android_usb/android0/enable写入失败。原因是init.rc执行时 USB PHY 尚未就绪。解决方案在on init段添加延时# 等待 USB PHY 就绪 exec_start wait_for_usb_phy # wait_for_usb_phy 是自定义 service执行脚本检测 /sys/class/usb_phy/*/state3.2 USB 串口通信跳过 UsbSerialDriver 的裸设备直通车载串口通信UsbSerialDriver库如usb-serial-for-android看似方便但在车规环境下是灾难。它依赖UsbManager.openDevice()而车载CarService会校验设备来源它内部使用bulkTransfer()易被 SELinux 拦截ioctl更致命的是它无法处理车规级的长时稳定传输——连续 24 小时运行后UsbDeviceConnection会莫名断开且无异常抛出。我的方案是绕过 Java 层用 JNI 直接读写/dev/ttyUSBx设备节点。前提是 SELinux 策略已开放权限见 2.4 节。C 代码核心逻辑// native-lib.cpp #include fcntl.h #include termios.h #include unistd.h extern C { JNIEXPORT jlong JNICALL Java_com_yourapp_UsbSerial_openDevice(JNIEnv *env, jobject thiz, jstring devicePath) { const char *path env-GetStringUTFChars(devicePath, nullptr); int fd open(path, O_RDWR | O_NOCTTY | O_SYNC); // O_SYNC 确保写入立即生效 if (fd 0) { __android_log_print(ANDROID_LOG_ERROR, UsbSerial, open %s failed: %s, path, strerror(errno)); return -1; } struct termios tty; if (tcgetattr(fd, tty) ! 0) { __android_log_print(ANDROID_LOG_ERROR, UsbSerial, tcgetattr error); close(fd); return -1; } cfsetospeed(tty, B115200); // 设置波特率 cfsetispeed(tty, B115200); tty.c_cflag ~PARENB; // 无校验位 tty.c_cflag ~CSTOPB; // 1 停止位 tty.c_cflag ~CSIZE; tty.c_cflag | CS8; // 8 数据位 tty.c_cflag ~CRTSCTS; // 无硬件流控 tty.c_cflag | CREAD | CLOCAL; // 启用接收忽略控制信号 tty.c_lflag ~ICANON; // 非规范模式 tty.c_lflag ~ECHO; // 不回显 tty.c_lflag ~ISIG; // 不生成信号 tty.c_iflag ~(IXON | IXOFF | IXANY); // 无软件流控 tty.c_oflag ~OPOST; // 原始输出 tty.c_cc[VMIN] 0; // 读取不阻塞 tty.c_cc[VTIME] 10; // 读取超时 1 秒 if (tcsetattr(fd, TCSANOW, tty) ! 0) { __android_log_print(ANDROID_LOG_ERROR, UsbSerial, tcsetattr error); close(fd); return -1; } env-ReleaseStringUTFChars(devicePath, path); return (jlong) fd; } JNIEXPORT jint JNICALL Java_com_yourapp_UsbSerial_readData(JNIEnv *env, jobject thiz, jlong fd, jbyteArray buffer) { uint8_t *buf (uint8_t*) env-GetByteArrayElements(buffer, nullptr); ssize_t len read((int) fd, buf, env-GetArrayLength(buffer)); env-ReleaseByteArrayElements(buffer, (jbyte*) buf, 0); return (jint) len; } }Java 层调用public class UsbSerial { static { System.loadLibrary(native-lib); } public static long openDevice(String devicePath) { return openDeviceNative(devicePath); } private static native long openDeviceNative(String devicePath); // 使用示例 public void startRead() { long fd openDevice(/dev/ttyUSB0); if (fd -1) return; byte[] buffer new byte[1024]; while (running) { int len readData(fd, buffer); if (len 0) { // 解析串口数据 parseOBDData(buffer, len); } } } }实操心得O_SYNC标志至关重要。车载 ECU 发送数据时若 USB 缓冲区未及时刷新会导致数据粘包。O_SYNC强制每次write()都等待硬件完成牺牲一点性能换来 100% 数据完整性。我测试过在 115200 波特率下O_SYNC带来的延迟增加不到 0.5ms完全可接受。3.3 USB-CAN 协议栈用 SocketCAN 替代 libusb 的零拷贝方案USB-CAN 适配器如周立功 USBCAN-2E-U、Peak PCAN-USB在车载系统中最头疼的问题是libusb的bulkTransfer()在高负载下丢帧严重。车规 CAN 总线要求 10ms 内完成一帧报文收发而libusb的 Java 层封装引入至少 3ms 的 JVM GC 延迟。解决方案是Kernel Space SocketCAN。Linux 内核 2.6.25 已内置can-dev和usb-can驱动可将 USB-CAN 设备注册为标准 CAN 接口如can0然后用socket(PF_CAN, SOCK_RAW, CAN_RAW)直接通信全程零拷贝。步骤确认内核支持adb shell zcat /proc/config.gz | grep -i can\|usb_can # 应看到 CONFIG_CANy, CONFIG_CAN_DEVy, CONFIG_USB_CANm adb shell ls /sys/bus/usb/drivers/usb-can/ # 若存在说明驱动已加载加载 USB-CAN 驱动# 插入设备后查看设备 ID adb shell lsusb # 输出Bus 001 Device 003: ID 0c72:000c PEAK-System Technik GmbH PCAN-USB Pro # 加载驱动以 PEAK 为例 adb shell insmod /vendor/lib/modules/peak_usb.ko adb shell ip link set can0 up type can bitrate 500000Java 层 SocketCAN 通信public class CanSocket { private FileDescriptor fd; public void openCanInterface(String iface) throws IOException { // 创建 CAN socket fd Os.socket(AF_CAN, SOCK_RAW, CAN_RAW, 0); // 获取接口索引 int ifindex Os.if_nametoindex(iface); // 绑定到接口 Struct sockaddr_can addr new Struct(); addr.can_family AF_CAN; addr.can_ifindex ifindex; Os.bind(fd, addr); } public void sendCanFrame(int id, byte[] data) throws IOException { // 构造 CAN 帧 byte[] frame new byte[16]; ByteBuffer bb ByteBuffer.wrap(frame); bb.putInt(id 0x1FFFFFFF); // 29 位扩展帧 ID bb.put((byte) data.length); // DLC bb.put(data); // 数据 Os.write(fd, frame, 0, frame.length); } }注意Os类来自libcore.io.Linux需反射调用。更稳妥的方式是用 JNI 封装socket()、bind()、sendto()。SocketCAN 的优势在于内核直接处理 USB DMAApp 层只需read()/write()无任何中间缓存实测 500kbps 下丢帧率为 0。3.4 HID 设备映射将方向盘按键转为 KeyEvent 的定制 InputMapper车载方向盘 HID 按键如音量、电话接听、语音唤醒在 Android 中默认不响应因为InputManagerService只处理KEYBOARD、MOUSE类 HID而方向盘按键常被识别为CONSUMER_CONTROL类Usage Page 0x0C。解决方案是定制 InputMapper在InputReader层将 HID 报文转换为KeyEvent。步骤获取 HID 设备节点adb shell getevent -p # 查找 HID 设备如 /dev/input/event2 adb shell getevent -l /dev/input/event2 # 按键时输出 # add device 2: /dev/input/event2 # name: HID 046d:c52b # /dev/input/event2: 0001 0073 00000001 # KEY_VOLUMEDOWN # /dev/input/event2: 0001 0073 00000000 # KEY_VOLUMEDOWN release编写 InputMapper在frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp中找到processEventsLocked()函数在switch (type)前插入if (device-getDeviceName().find(HID) ! std::string::npos) { if (type EV_KEY code KEY_VOLUMEDOWN) { // 转发为自定义 KeyEvent NotifyKeyArgs args(when, getDisplayId(), deviceId, 0, AKEY_EVENT_ACTION_DOWN, AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, metaState, repeatCount, downTime); notifyKey(args); } }App 层监听public class SteeringWheelReceiver extends BroadcastReceiver { Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) { // 注册全局按键监听 KeyguardManager km (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); if (km.isKeyguardLocked()) { // 锁屏状态下处理方向盘按键 handleSteeringKey(intent.getIntExtra(key_code, 0)); } } } }实操心得不要试图用InputDeviceAPI 获取 HID 设备。车载系统中InputDevice.getDeviceIds()返回的列表不包含 HID 设备因为InputManagerService在addDeviceLocked()时已过滤掉非白名单设备。定制 InputMapper 是唯一可靠方案虽然要改 AOSP但一次投入永久生效。4. 系统级集成与调试CarService、ADB 与 SELinux 的协同作战车载 USB 开发的终极挑战不是单个模块跑通而是让所有模块在车规环境下稳定协同。这要求你深入CarService、熟练使用 ADB 调试技巧、并能快速定位 SELinux 策略问题。下面分享我在量产项目中沉淀的三套组合拳。4.1 CarService 深度集成USB 设备生命周期管理CarService是车载 Android 的中枢神经USB 设备的整个生命周期插入、权限申请、配置、断开都由它调度。第三方 App 必须通过ICarUsbService接口与之交互而非直接调用UsbManager。关键接口registerUsbDeviceCallback(ICarUsbCallback callback)注册设备状态回调requestUsbPermission(UsbDevice device, String packageName)申请设备权限openUsbDevice(UsbDevice device)打开设备连接getUsbDeviceList()获取当前已授权设备列表。实操代码public class CarUsbHelper { private ICarUsbService mUsbService; private ICarUsbCallback mCallback; public void init(Context context) { // 获取 CarService 实例 Car car Car.createCar(context); mUsbService ICarUsbService.Stub.asInterface( ServiceManager.getService(car_usb)); // 注册回调 mCallback new ICarUsbCallback.Stub() { Override public void onUsbDeviceAttached(UsbDevice device) { Log.d(CarUsb, Device attached: device.getDeviceName()); // 立即申请权限 mUsbService.requestUsbPermission(device, context.getPackageName()); } Override public void onUsbPermissionGranted(UsbDevice device) { Log.d(CarUsb, Permission granted for device.getDeviceName()); // 打开设备 UsbDeviceConnection conn mUsbService.openUsbDevice(device); if (conn ! null) { // 启动数据读取线程 startReading(conn, device); } } }; mUsbService.registerUsbDeviceCallback(mCallback); } }注意Car.createCar()需要android.car.Car权限且 App 必须声明uses-permission android:nameandroid.car.permission.CAR_CONTROL /。车厂还需在car_product_config.xml中添加usb-device class0x03 subclass0x00 protocol0x00 /否则onUsbDeviceAttached()不会触发。4.2 ADB 调试黄金命令定位 USB 问题的七把刀车载环境 ADB 权限受限但以下命令是定位 USB 问题的利器我整理成速查表命令作用典型输出与解读adb shell dumpsys usb查看 USB 服务全局状态mUsbHalService: ...表示 HAL 是否正常mDeviceList: {...}显示已识别设备mPermissionMap: {0403:6001true}表示权限已授予adb shell getprop | grep usb查看 USB 相关属性sys.usb.configmtp,adb表示当前配置sys.usb.stateCONFIGURED表示已就绪ro.boot.usbconfignone表示内核锁定adb shell ls /sys/bus/usb/devices/列出内核识别的 USB 设备若为空说明 USB PHY 未供电或内核驱动未加载若有1-1但无1-1.1说明设备未枚举成功adb shell dmesg | grep -i usb|cdc|hid查看内核 USB 日志[ 4.231245] usbcore: registered new interface driver cdc_acm表示 CDC 驱动已注册[ 5.678901] cdc_acm 1-1.1:1.0: ttyACM0: USB ACM device表示设备已绑定到/dev/ttyACM0adb shell cat /sys/bus/usb/devices/*/bDeviceClass查看各设备类码0x02是 CDC 类串口0x03是 HID 类0xff是厂商自定义类需额外驱动adb shell ls -l /dev/tty*查看串口设备节点crw-rw---- 1 root dialout 188, 0 2023-01-01 00:00 /dev/ttyUSB0表示权限为root:dialoutApp 需加入dialout组或改 SELinux 策略adb shell dmesg | grep avc | grep usb查看 SELinux 拦截日志avc: denied { open } for ... tcontextu:object_r:usb_device_file:s0表示 SELinux 拒绝访问设备节点实操心得dumpsys usb是第一排查命令。若mDeviceList为空直接跳过 App 层代码去查内核和 HAL若mPermissionMap中设备 ID 为false说明权限申请失败检查CarService是否收到回调若mUsbHalService为null说明 HAL 服务未启动需查/vendor/etc/init/hw/init.rc中usb_hal_service是否 enable。4.3 SELinux 策略调试从 avc denied 到策略生效的闭环SELinux 是车载 USB 开发的“最后一公里”。avc denied日志明确告诉你哪里被拦但如何写策略、如何加载、如何验证是工程师的必修课。完整流程捕获 avc 日志adb shell dmesg | grep avc avc.log # 或实时监控 adb shell dmesg -w | grep avc生成策略模板使用audit2allow工具需 Linux 主机# 将 avc.log 传到电脑 adb pull /data/local/tmp/avc.log # 生成策略 audit2allow -i avc.log -M myusb # 输出 myusb.te 文件手动精简策略强烈推荐audit2allow生成的策略过于宽泛。例如# 自动生成危险 allow untrusted_app usb_device_file:chr_file { open read write ioctl getattr }; # 手动精简安全 allow your_app usb_device_file:chr_file { open read write }; # 删除 ioctl因串口通信无需 ioctl编译并加载策略# 在车机上 adb push myusb.te /data/local/tmp/ adb shell sepolicy-inject -s your_app -t usb_device_file -c chr_file -p open,read,write -l # 或编译为 .cil 并加载 adb shell sepolicy-inject -f /data/local/tmp/myusb.te -l验证策略生效adb shell dmesg | grep avc # 若无新 avc 日志且 App 功能正常说明策略生效 # 检查策略是否加载 adb shell sesearch -A -s your_app -t usb_device_file注意sepolicy-inject需要root权限且车载系统通常禁用adb root。此时需车厂提供sepolicy-inject的系统签名版本或通过recovery模式刷入策略补丁。策略必须精确到scontext源上下文和tcontext目标上下文模糊匹配如allow appdomain ...在车规系统中会被安全审计驳回。5. 常见问题与独家避坑指南来自产线的 12 个血泪教训最后分享我在 7 个车载项目中踩过的坑。这些不是理论问题而是量产线上真实发生的、让工程师加班到凌晨三点的故障。每个问题都附带根因分析和可落地的解决方案。5.1 问题 1USB-CAN 设备插入后lsusb能看到但ip link show can0无输出现象adb shell lsusb显示设备dmesg有usb 1-1.1: new full-speed USB device但ip link show can0报错Device can0 does not exist。根因USB-CAN 驱动未加载或设备 VID/PID 不在内核
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →