libusb与移动设备USB OTG通信:Android平台实现方案

libusb与移动设备USB OTG通信:Android平台实现方案

【免费下载链接】libusb A cross-platform library to access USB devices 【免费下载链接】libusb 项目地址: https://gitcode.com/gh_mirrors/li/libusb

引言:移动USB通信的痛点与解决方案

你是否曾面临这样的开发困境:需要在Android设备上与USB外设通信,却受限于系统权限和平台差异?传统方案要么需要root权限,要么依赖厂商定制API,兼容性和安全性难以兼顾。本文将系统讲解如何基于libusb库,在非root的Android设备上通过USB OTG(On-The-Go)实现稳定可靠的USB通信,涵盖从环境搭建到数据传输的完整流程。

读完本文你将获得:

  • 理解Android USB OTG通信的底层原理
  • 掌握libusb在Android平台的移植与配置方法
  • 实现非root设备的USB设备枚举与数据传输
  • 解决常见的权限问题和兼容性挑战
  • 完整的代码示例与调试技巧

1. 核心概念与架构设计

1.1 USB OTG与libusb技术解析

USB OTG(On-The-Go)是USB 2.0规范的补充标准,允许设备在主机(Host)和外设(Peripheral)角色间动态切换。在Android设备上,这一特性使得手机或平板能够直接与USB设备通信,无需依赖计算机作为中介。

libusb是一个跨平台的USB设备访问库,提供统一的API抽象,屏蔽了不同操作系统底层USB实现的差异。其核心优势在于:

  • 跨平台兼容性(Windows/macOS/Linux/Android等)
  • 用户态实现,无需内核驱动开发
  • 支持USB 1.0至USB4的全协议栈
  • 丰富的端点类型和传输模式支持

1.2 Android平台USB通信架构

Android系统采用分层架构实现USB通信:

mermaid

关键技术点:

  • Linux内核提供基础USB设备枚举和数据传输能力
  • Android Framework层的UsbManager处理权限管理和设备发现
  • libusb通过libusb_wrap_sys_device函数绑定系统文件描述符
  • JNI/NDK桥接Native代码与Java应用层

2. 开发环境搭建

2.1 必要工具与依赖

工具/依赖版本要求用途
Android Studio4.0+应用开发IDE
NDK21.0+原生代码编译
CMake3.10+构建脚本
libusb源码1.0.24+USB通信核心库
Android设备API 21+ (Android 5.0+)运行测试环境

2.2 源码获取与项目配置

  1. 克隆libusb仓库:
git clone https://gitcode.com/gh_mirrors/li/libusb
  1. 在Android项目中配置NDK支持,修改app/build.gradle
android {
    ...
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
        }
    }
    ndkVersion "21.4.7075529"
}
  1. 创建CMakeLists.txt文件:
cmake_minimum_required(VERSION 3.10.2)
project("libusb_android_demo")

# 添加libusb源码
add_library(libusb STATIC
        ../../../../libusb/libusb/core.c
        ../../../../libusb/libusb/descriptor.c
        ../../../../libusb/libusb/hotplug.c
        ../../../../libusb/libusb/io.c
        ../../../../libusb/libusb/strerror.c
        ../../../../libusb/libusb/sync.c
        ../../../../libusb/libusb/os/linux_usbfs.c
        ../../../../libusb/libusb/os/linux_udev.c
        ../../../../libusb/libusb/os/threads_posix.c
        ../../../../libusb/libusb/os/events_posix.c)

target_include_directories(libusb PUBLIC
        ../../../../libusb/libusb
        ../../../../libusb/libusb/os)

# 编译应用原生库
add_library(native-lib SHARED native-lib.cpp)

target_link_libraries(native-lib
        libusb
        log)

3. 权限处理与设备发现

3.1 Android USB权限机制

Android通过权限系统控制USB设备访问,应用必须获得用户授权才能与USB设备通信。关键步骤:

  1. AndroidManifest.xml中声明权限:
<uses-permission android:name="android.permission.USB_PERMISSION" />
<uses-feature android:name="android.hardware.usb.host" android:required="true" />
  1. 注册USB设备连接广播接收器:
private static final String ACTION_USB_PERMISSION = "com.example.USB_PERMISSION";
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (ACTION_USB_PERMISSION.equals(action)) {
            synchronized (this) {
                UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    if(device != null){
                        // 权限已授予,打开设备连接
                        connectToDevice(device);
                    }
                } 
            }
        }
    }
};
  1. 请求USB设备权限:
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
UsbDevice device = ...; // 获取目标USB设备
PendingIntent permissionIntent = PendingIntent.getBroadcast(this, 0, 
        new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);
usbManager.requestPermission(device, permissionIntent);

3.2 设备枚举与文件描述符获取

当权限获得后,通过UsbDeviceConnection获取系统文件描述符:

private int getUsbFileDescriptor(UsbDevice device) {
    UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
    UsbInterface intf = device.getInterface(0);
    
    // 打开设备连接
    UsbDeviceConnection connection = usbManager.openDevice(device);
    if (!connection.claimInterface(intf, true)) {
        Log.e(TAG, "无法获取接口控制权");
        return -1;
    }
    
    // 获取文件描述符
    return connection.getFileDescriptor();
}

4. libusb移植与初始化

4.1 libusb Android适配关键代码

libusb在Android平台的初始化需要特殊配置,关键步骤包括禁用设备发现和包装系统文件描述符:

#include <libusb.h>
#include <android/log.h>

#define LOG_TAG "LibUsbAndroid"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)

int initialize_libusb(int fileDescriptor) {
    libusb_context *ctx = NULL;
    libusb_device_handle *devh = NULL;
    int r;
    
    // 禁用设备发现(Android不需要枚举系统USB设备)
    r = libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY, NULL);
    if (r != LIBUSB_SUCCESS) {
        LOGD("libusb_set_option failed: %d", r);
        return r;
    }
    
    // 初始化libusb上下文
    r = libusb_init(&ctx);
    if (r < 0) {
        LOGD("libusb_init failed: %d", r);
        return r;
    }
    
    // 包装系统文件描述符
    r = libusb_wrap_sys_device(ctx, (intptr_t)fileDescriptor, &devh);
    if (r < 0) {
        LOGD("libusb_wrap_sys_device failed: %d", r);
        libusb_exit(ctx);
        return r;
    }
    
    // 设备初始化成功,可以进行通信操作
    LOGD("libusb设备初始化成功");
    return 0;
}

4.2 JNI桥接实现

创建JNI接口,实现Java与Native代码通信:

public class LibUsbWrapper {
    // 加载Native库
    static {
        System.loadLibrary("usb_communication");
    }
    
    // 声明Native方法
    public native int initializeUsb(int fileDescriptor);
    public native int readData(byte[] buffer, int length);
    public native int writeData(byte[] buffer, int length);
    public native void closeUsb();
}

对应的JNI实现:

JNIEXPORT jint JNICALL
Java_com_example_usb_LibUsbWrapper_initializeUsb(JNIEnv *env, jobject thiz, jint file_descriptor) {
    return initialize_libusb(file_descriptor);
}

// 其他JNI方法实现...

5. 数据传输实现

5.1 USB端点配置与传输模式

USB通信通过端点(Endpoint)进行,常见端点类型包括:

  • 控制端点(Control Endpoint):用于设备配置和管理
  • 批量端点(Bulk Endpoint):用于大量数据传输,可靠但不保证实时性
  • 中断端点(Interrupt Endpoint):用于小量实时数据传输
  • 等时端点(Isochronous Endpoint):用于实时流媒体传输,保证带宽但不保证可靠性

配置端点示例代码:

// 查找批量输入端点
struct libusb_config_descriptor *config;
libusb_get_config_descriptor(dev, 0, &config);
for (int i = 0; i < config->interface[0].altsetting[0].bNumEndpoints; i++) {
    struct libusb_endpoint_descriptor *ep = &config->interface[0].altsetting[0].endpoint[i];
    if ((ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN &&
        (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK) {
        bulk_in_endpoint = ep->bEndpointAddress;
        max_packet_size = ep->wMaxPacketSize;
        break;
    }
}
libusb_free_config_descriptor(config);

5.2 同步数据传输实现

使用libusb同步传输函数实现简单数据读写:

// 读取数据
JNIEXPORT jint JNICALL
Java_com_example_usb_LibUsbWrapper_readData(JNIEnv *env, jobject thiz, jbyteArray buffer, jint length) {
    jbyte *data = (*env)->GetByteArrayElements(env, buffer, NULL);
    int transferred;
    
    // 同步读取数据
    int r = libusb_bulk_transfer(devh, bulk_in_endpoint, (unsigned char*)data, 
                                 length, &transferred, 1000);
    
    (*env)->ReleaseByteArrayElements(env, buffer, data, 0);
    return (r == LIBUSB_SUCCESS) ? transferred : r;
}

// 写入数据
JNIEXPORT jint JNICALL
Java_com_example_usb_LibUsbWrapper_writeData(JNIEnv *env, jobject thiz, jbyteArray buffer, jint length) {
    jbyte *data = (*env)->GetByteArrayElements(env, buffer, NULL);
    int transferred;
    
    // 同步写入数据
    int r = libusb_bulk_transfer(devh, bulk_out_endpoint, (unsigned char*)data, 
                                 length, &transferred, 1000);
    
    (*env)->ReleaseByteArrayElements(env, buffer, data, 0);
    return (r == LIBUSB_SUCCESS) ? transferred : r;
}

5.3 异步数据传输实现

对于需要高吞吐量或低延迟的场景,异步传输更为适合:

// 异步传输上下文
typedef struct {
    libusb_transfer *transfer;
    unsigned char *buffer;
    jbyteArray jbuffer;
    JNIEnv *env;
    jobject callback;
} AsyncTransferContext;

// 异步传输完成回调
void transfer_completed(struct libusb_transfer *transfer) {
    AsyncTransferContext *ctx = transfer->user_data;
    
    // 调用Java回调函数
    jclass cls = (*ctx->env)->GetObjectClass(ctx->env, ctx->callback);
    jmethodID mid = (*ctx->env)->GetMethodID(ctx->env, cls, "onTransferCompleted", "(I[B)V");
    if (mid) {
        (*ctx->env)->CallVoidMethod(ctx->env, ctx->callback, transfer->actual_length, ctx->jbuffer);
    }
    
    // 释放资源
    (*ctx->env)->DeleteLocalRef(ctx->env, cls);
    free(ctx->buffer);
    free(ctx);
    libusb_free_transfer(transfer);
}

// 发起异步读取
JNIEXPORT void JNICALL
Java_com_example_usb_LibUsbWrapper_startAsyncRead(JNIEnv *env, jobject thiz, jint length, jobject callback) {
    AsyncTransferContext *ctx = malloc(sizeof(AsyncTransferContext));
    ctx->transfer = libusb_alloc_transfer(0);
    ctx->buffer = malloc(length);
    ctx->jbuffer = (*env)->NewByteArray(env, length);
    ctx->env = env;
    ctx->callback = (*env)->NewGlobalRef(env, callback);
    
    // 初始化异步传输
    libusb_fill_bulk_transfer(ctx->transfer, devh, bulk_in_endpoint,
                             ctx->buffer, length, transfer_completed, ctx, 1000);
    
    // 提交异步传输
    libusb_submit_transfer(ctx->transfer);
    
    // 启动libusb事件处理线程
    // ... (省略线程启动代码)
}

5. 设备描述符解析与枚举

5.1 USB设备描述符结构

USB设备描述符包含设备的基本信息,解析代码示例:

void print_device_descriptor(libusb_device *dev) {
    struct libusb_device_descriptor desc;
    int r = libusb_get_device_descriptor(dev, &desc);
    if (r < 0) {
        LOGD("获取设备描述符失败");
        return;
    }
    
    LOGD("设备描述符信息:");
    LOGD("  厂商ID: 0x%04X", desc.idVendor);
    LOGD("  产品ID: 0x%04X", desc.idProduct);
    LOGD("  USB版本: %d.%d", (desc.bcdUSB >> 8), (desc.bcdUSB & 0xFF));
    LOGD("  设备版本: %d.%d", (desc.bcdDevice >> 8), (desc.bcdDevice & 0xFF));
    LOGD("  设备类别: 0x%02X", desc.bDeviceClass);
    LOGD("  配置数量: %d", desc.bNumConfigurations);
}

5.2 配置描述符与接口解析

配置描述符包含设备的配置信息,接口描述符定义设备的功能接口:

void print_configuration_descriptor(libusb_device *dev) {
    struct libusb_config_descriptor *config;
    int r = libusb_get_config_descriptor(dev, 0, &config);
    if (r < 0) {
        LOGD("获取配置描述符失败");
        return;
    }
    
    LOGD("配置描述符信息:");
    LOGD("  配置值: %d", config->bConfigurationValue);
    LOGD("  接口数量: %d", config->bNumInterfaces);
    
    // 遍历接口
    for (int i = 0; i < config->bNumInterfaces; i++) {
        const struct libusb_interface *interface = &config->interface[i];
        LOGD("  接口 %d 备选设置数量: %d", i, interface->num_altsetting);
        
        // 遍历备选设置
        for (int j = 0; j < interface->num_altsetting; j++) {
            const struct libusb_interface_descriptor *altsetting = &interface->altsetting[j];
            LOGD("    备选设置 %d:", j);
            LOGD("      接口类别: 0x%02X", altsetting->bInterfaceClass);
            LOGD("      接口子类: 0x%02X", altsetting->bInterfaceSubClass);
            LOGD("      协议: 0x%02X", altsetting->bInterfaceProtocol);
            LOGD("      端点数量: %d", altsetting->bNumEndpoints);
            
            // 遍历端点
            for (int k = 0; k < altsetting->bNumEndpoints; k++) {
                const struct libusb_endpoint_descriptor *endpoint = &altsetting->endpoint[k];
                LOGD("        端点 %d:", k);
                LOGD("          地址: 0x%02X", endpoint->bEndpointAddress);
                LOGD("          属性: 0x%02X", endpoint->bmAttributes);
                LOGD("          最大包大小: %d", endpoint->wMaxPacketSize);
                LOGD("          间隔: %d", endpoint->bInterval);
            }
        }
    }
    
    libusb_free_config_descriptor(config);
}

5.3 设备信息解析示例

结合上述函数,完整的设备信息解析流程:

int parse_usb_device_info(int fileDescriptor) {
    libusb_context *ctx = NULL;
    libusb_device_handle *devh = NULL;
    int r;
    
    // 初始化libusb并包装文件描述符
    // ... (省略初始化代码,同4.1节)
    
    // 获取设备信息并打印
    libusb_device *dev = libusb_get_device(devh);
    print_device_descriptor(dev);
    print_configuration_descriptor(dev);
    
    // 关闭设备
    libusb_close(devh);
    libusb_exit(ctx);
    return 0;
}

6. 错误处理与调试技巧

6.1 常见错误码及解决方案

错误码含义可能原因解决方案
LIBUSB_ERROR_ACCESS权限不足未获取USB权限确保已请求并获得USB权限
LIBUSB_ERROR_NO_DEVICE设备不存在USB设备已断开检查设备连接状态
LIBUSB_ERROR_BUSY设备忙接口被占用确保正确释放接口资源
LIBUSB_ERROR_TIMEOUT传输超时设备无响应增加超时时间或检查设备
LIBUSB_ERROR_PIPE管道错误端点配置错误检查端点地址和类型

6.2 调试工具与方法

  1. 使用Android Logcat查看调试信息:
adb logcat -s "LibUsbAndroid"
  1. 启用libusb详细日志:
libusb_set_debug(ctx, LIBUSB_LOG_LEVEL_DEBUG);
  1. 使用USB协议分析仪监控总线上的数据传输

  2. 检查设备权限和文件描述符:

adb shell ls -l /proc/[pid]/fd

7. 性能优化与最佳实践

7.1 传输性能优化策略

  1. 批量传输优化

    • 使用最大包大小(wMaxPacketSize)
    • 合理设置传输超时时间
    • 实现传输缓冲区池管理
  2. 异步传输优化

    • 预分配传输结构和缓冲区
    • 使用多传输队列重叠操作
    • 合理设置传输完成回调处理逻辑
  3. 电源管理

    • 避免频繁设备枚举
    • 闲置时释放USB接口
    • 使用USB低功耗模式

7.2 多线程安全设计

USB通信操作应在独立线程中执行,避免阻塞UI线程:

// Android应用中的通信线程示例
private class UsbCommunicationThread extends Thread {
    private volatile boolean running = true;
    private int fileDescriptor;
    
    public UsbCommunicationThread(int fd) {
        this.fileDescriptor = fd;
    }
    
    @Override
    public void run() {
        LibUsbWrapper usbWrapper = new LibUsbWrapper();
        int result = usbWrapper.initializeUsb(fileDescriptor);
        if (result != 0) {
            // 初始化失败处理
            return;
        }
        
        byte[] buffer = new byte[512];
        while (running) {
            int bytesRead = usbWrapper.readData(buffer, buffer.length);
            if (bytesRead > 0) {
                // 处理接收到的数据
                // ...
            } else if (bytesRead < 0) {
                // 错误处理
                // ...
            }
        }
        
        usbWrapper.closeUsb();
    }
    
    public void stopThread() {
        running = false;
    }
}

7.3 兼容性处理

  1. 设备兼容性

    • 处理不同厂商设备的描述符差异
    • 实现设备特定的初始化逻辑
    • 支持多种USB设备类别
  2. 系统版本兼容性

    • 针对不同Android版本适配API
    • 处理不同NDK版本差异
    • 支持64位和32位系统架构

8. 完整示例应用

8.1 应用架构

mermaid

8.2 核心代码实现

Java层主要代码

public class MainActivity extends AppCompatActivity {
    private UsbManagerWrapper usbManagerWrapper;
    private CommunicationThread commThread;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        usbManagerWrapper = new UsbManagerWrapper(this);
        usbManagerWrapper.registerUsbDeviceCallback(new UsbManagerWrapper.DeviceCallback() {
            @Override
            public void onDeviceConnected(int fileDescriptor) {
                // 启动通信线程
                commThread = new CommunicationThread(fileDescriptor);
                commThread.start();
            }
            
            @Override
            public void onDeviceDisconnected() {
                // 停止通信线程
                if (commThread != null) {
                    commThread.stopThread();
                    commThread = null;
                }
            }
        });
        
        // 请求USB权限
        usbManagerWrapper.requestUsbPermission();
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (commThread != null) {
            commThread.stopThread();
        }
        usbManagerWrapper.unregisterUsbDeviceCallback();
    }
}

Native层主要代码

// JNI接口实现
JNIEXPORT jint JNICALL
Java_com_example_usb_LibUsbWrapper_initializeUsb(JNIEnv *env, jobject thiz, jint file_descriptor) {
    return initialize_libusb(file_descriptor);
}

JNIEXPORT jint JNICALL
Java_com_example_usb_LibUsbWrapper_readData(JNIEnv *env, jobject thiz, jbyteArray buffer, jint length) {
    // 实现数据读取,同5.2节
}

JNIEXPORT jint JNICALL
Java_com_example_usb_LibUsbWrapper_writeData(JNIEnv *env, jobject thiz, jbyteArray buffer, jint length) {
    // 实现数据写入,同5.2节
}

JNIEXPORT void JNICALL
Java_com_example_usb_LibUsbWrapper_closeUsb(JNIEnv *env, jobject thiz) {
    // 关闭设备并释放资源
    if (devh) {
        libusb_close(devh);
        devh = NULL;
    }
    if (ctx) {
        libusb_exit(ctx);
        ctx = NULL;
    }
}

9. 总结与展望

9.1 关键知识点回顾

本文详细介绍了在Android平台使用libusb实现USB OTG通信的完整方案,包括:

  • Android USB OTG通信架构与原理
  • libusb库移植与初始化方法
  • 设备枚举、描述符解析和数据传输实现
  • 错误处理、调试技巧和性能优化策略
  • 完整示例应用与最佳实践

通过这种方案,开发者可以在非root的Android设备上实现与USB外设的可靠通信,克服了传统方案的权限限制和兼容性问题。

9.2 未来发展方向

  1. USB4与雷电协议支持:随着USB4和雷电(Thunderbolt)技术的普及,未来移动设备将支持更高带宽的USB通信。

  2. WebUSB API集成:结合WebUSB API,实现Web应用与USB设备的直接通信。

  3. USB-C扩展功能:利用USB-C的Alternate Mode,实现视频输出、高速数据传输等扩展功能。

  4. AI辅助调试:通过机器学习算法分析USB通信日志,自动识别和诊断问题。

10. 参考资料与进一步学习


如果本文对你的开发工作有所帮助,请点赞、收藏并关注,以便获取更多Android USB开发进阶内容。下期将带来"libusb多设备并发通信与冲突解决策略",敬请期待!

【免费下载链接】libusb A cross-platform library to access USB devices 【免费下载链接】libusb 项目地址: https://gitcode.com/gh_mirrors/li/libusb

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值