HarmonyOS 鸿蒙Next中libusb移植总结

HarmonyOS 鸿蒙Next中libusb移植总结

【背景知识】 Windows上使用OpenHarmony SDK交叉编译指导(https://gitcode.com/openharmony-sig/tpc_c_cplusplus/blob/master/docs/adapter_windows.md)。

【解决方案】 libusb可以移植到HarmoneyOS,但是libusb因为涉及到权限问题,必须通过应用层的usbManager(https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-usbmanager)获取到usb的句柄传到native层去使用。参考外设扩展驱动开发指导(https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/driverextensionability#%E5%BA%94%E7%94%A8%E7%AD%BE%E5%90%8D)中的ohos.permission.ACCESS_EXTENSIONAL_DEVICE_DRIVER权限。

libusb集成到应用hap,可参考:libusb集成到应用hap(https://gitcode.com/openharmony-sig/tpc_c_cplusplus/blob/master/thirdparty/libusb/docs/hap_integrate.md),

编译产物也适用于HarmonyOS Next。

示例代码如下:

import { hilog } from '@kit.PerformanceAnalysisKit';
import testNapi from 'libentry.so';
import { usbManager } from '@kit.BasicServicesKit';
import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  showToast(msg: string) {
    promptAction.showToast({
      message: msg,
      duration: 1500,
    });
  }

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            let deviceList: Array<usbManager.USBDevice> = usbManager.getDevices();
            if (deviceList.length <= 0) {
              this.showToast('未发现龙脉Ukey!');
              return;
            }

            console.log(`devicesList = ${JSON.stringify(deviceList)}`);

            let find = false;
            let index = 0;

            for (let dev of deviceList) {
              if (dev.vendorId == 1372) {
                if (!usbManager.hasRight(dev.name)) {
                  usbManager.requestRight(dev.name);
                }
                find = true;
                break;
              }
              index++;
            }

            let devicepipe: usbManager.USBDevicePipe = usbManager.connectDevice(deviceList[index]);
            let fd: number = usbManager.getFileDescriptor(devicepipe);
            testNapi.skf_TestLed(fd);

            this.showToast(
              index + " : " + fd + " " + deviceList[0].vendorId + " " + deviceList[index].vendorId
            );
          });
      }
      .width('100%')
    }
    .height('100%');
  }
}
static napi_value NAPI_Global_skf_TestLed(napi_env env, napi_callback_info info) {
    napi_value result;
    size_t argc = 1;
    napi_value argv;

    napi_get_cb_info(env, info, &argc, &argv, nullptr, nullptr);

    int32_t fd = 0;
    napi_get_value_int32(env, argv, &fd);

    OH_LOG_INFO(LOG_APP, "fd: %{public}d", fd);

    static libusb_context *ctx = NULL;
    if (ctx == NULL) {
        struct libusb_init_option options[] = {
            {
                .option = LIBUSB_OPTION_NO_DEVICE_DISCOVERY,
                .value = { .ival = NULL },
            },
        };

        OH_LOG_INFO(LOG_APP, "%{public}s %{public}s", "libusb_init1", "start");
        int r = libusb_init_context(&ctx, options, 1);
        OH_LOG_INFO(LOG_APP, "%{public}s %{public}d", "libusb_init2", r);

        if (r < 0) {
            OH_LOG_INFO(LOG_APP, "%{public}s %d", "libusb_initfail", libusb_error_name(r));
        }
    }

    libusb_device_handle *handle = NULL;
    int r = libusb_wrap_sys_device(ctx, (intptr_t)fd, &handle);
    OH_LOG_INFO(LOG_APP, "%{public}s %{public}d", "libusb_wrap_sys_device", r);

    if (r == 0) {
        OH_LOG_INFO(LOG_APP, "%{public}s", "libusb_wrap_sys_device ok");
    }

    napi_get_undefined(env, &result);

    // 获取设备列表
    libusb_device **devices;
    ssize_t cnt = libusb_get_device_list(ctx, &devices);
    if (cnt < 0) {
        std::cerr << "Failed to get device list: " << libusb_error_name(cnt) << std::endl;
        libusb_exit(ctx);
    }

    // 遍历设备列表并获取第一个设备
    for (ssize_t i = 0; i < cnt; i++) {
        libusb_device *device = devices[i];
        libusb_device_descriptor desc;

        r = libusb_get_device_descriptor(device, &desc);
        if (r < 0) {
            std::cerr << "Failed to get device descriptor: " << libusb_error_name(r) << std::endl;
            continue;
        }

        // 打印设备信息
        std::cout << "Device " << i << ": ID " 
                  << std::hex << desc.idVendor << ":" 
                  << desc.idProduct << std::dec << std::endl;

        // 打开设备
        libusb_device_handle *handle;
        r = libusb_open(device, &handle);
        if (r != 0) {
            std::cerr << "Failed to open device: " << libusb_error_name(r) << std::endl;
            continue;
        }

        // 关闭设备
        libusb_close(handle);
        break; // 只处理第一个设备
    }

    // 释放设备列表
    libusb_free_device_list(devices, 1);

    // 退出 libusb
    libusb_exit(ctx);

    return result;
}

权限配置:

"requestPermissions":[ { "name": "ohos.permission.ACCESS_EXTENSIONAL_DEVICE_DRIVER" } ] ​

更多关于HarmonyOS 鸿蒙Next中libusb移植总结的实战教程也可以访问 https://www.itying.com/category-93-b0.html

3 回复

关注

更多关于HarmonyOS 鸿蒙Next中libusb移植总结的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


鸿蒙Next中移植libusb需关注以下几点:

  1. 使用HDF驱动框架替代传统Linux USB驱动交互
  2. 适配鸿蒙USB API接口,重点关注UsbClient、UsbPipe等核心类
  3. 修改libusb上下文初始化流程,替换udev相关代码为鸿蒙设备枚举方式
  4. 重新实现异步I/O机制,利用鸿蒙事件通知机制替代epoll
  5. 处理权限问题,需在config.json中声明ohos.permission.USB权限

移植后的libusb可支持标准USB设备操作,但需注意鸿蒙USB主机模式的限制。

从技术实现来看,这个方案已经较好地解决了libusb在HarmonyOS Next上的移植问题。关键点在于:

  1. 权限处理必须通过应用层的usbManager获取USB设备句柄,再传递到native层使用。这是HarmonyOS的安全机制要求。

  2. 代码示例展示了完整的实现流程:

  • 前端通过usbManager获取设备列表和文件描述符
  • 通过NAPI将文件描述符传递给native层
  • native层使用libusb_wrap_sys_device将文件描述符转换为libusb可操作的handle
  1. 需要特别注意权限配置,必须声明ohos.permission.ACCESS_EXTENSIONAL_DEVICE_DRIVER权限。

  2. 移植时需注意libusb_init_context的初始化选项设置,示例中使用了LIBUSB_OPTION_NO_DEVICE_DISCOVERY以避免冲突。

这个方案充分利用了HarmonyOS的USB管理API和NAPI桥接机制,实现了标准libusb库的功能兼容,是较为规范的移植实践。

回到顶部