Android 13 Camera HAL启动流程(2)

文章目录


欢迎关注微信公众号 无限无羡

上一节Android 13 Camera HAL启动流程(1)主要介绍了CameraProvider进程启动后加载[email protected]库并执行LegacyCameraProviderImpl_2_4的初始化函数,本节从LegacyCameraProviderImpl_2_4的初始化流程开始继续往下讲解。

// hardware/interfaces/camera/provider/2.4/default/LegacyCameraProviderImpl_2_4.cpp
LegacyCameraProviderImpl_2_4::LegacyCameraProviderImpl_2_4() :
        camera_module_callbacks_t({
    
    sCameraDeviceStatusChange,
                                   sTorchModeStatusChange}) {
    
    
    mInitFailed = initialize();
}

// initialize()函数的实现
bool LegacyCameraProviderImpl_2_4::initialize() {
    
    
    camera_module_t *rawModule;
    // 1. 通过hw_get_module加载对应的so库,这个库是由厂商提供的,一般只以so库的方式提供,
    // 不过也有情况会提供so库的源码,不过核心的地方还是会封装在别的so库中。
    int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID,
            (const hw_module_t **)&rawModule);
    if (err < 0) {
    
    
        ALOGE("Could not load camera HAL module: %d (%s)", err, strerror(-err));
        return true;
    }
	// 这里先省略后面的代码,将hw_get_module介绍完后我们再返回来继续
	...
}

下面开始介绍hw_get_module函数。
第一个参数为CAMERA_HARDWARE_MODULE_ID ,定义的地方为
第二个参数为出参

// hardware/libhardware/include/hardware/camera_common.h
#define CAMERA_HARDWARE_MODULE_ID "camera"
// hardware/libhardware/hardware.c
int hw_get_module(const char *id, const struct hw_module_t **module)
{
    
    
    return hw_get_module_by_class(id, NULL, module);
}
int hw_get_module_by_class(const char *class_id, const char *inst,
                           const struct hw_module_t **module)d_name
{
    
    
    int i = 0;
    char prop[PATH_MAX] = {
    
    0};
    char path[PATH_MAX] = {
    
    0};
    char name[PATH_MAX] = {
    
    0};
    char prop_name[PATH_MAX] = {
    
    0};

	// inst为NULL, 即为false
    if (inst)
        snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
    else
    	// name 为 “camera”
        strlcpy(name, class_id, PATH_MAX);

    /*
     * Here we rely on the fact that calling dlopen multiple times on
     * the same .so will simply increment a refcount (and not load
     * a new copy of the library).
     * We also assume that dlopen() is thread-safe.
     */

    /* First try a property specific to the class and possibly instance */
    // 查看ro.hardware.camera属性值是否为空,这个实际上定义了camera hal so库的名称
    // 这个so库是厂商专门封装的so库,这样可以将自己的核心代码保持闭源
    // 最后的标准名称应为path/camera.xxx.so, 这里的xxx就是prop属性值.
    // 对于这个so库,google提供了一个默认实现,
    // 位于hardware/libhardware/modules/camera/3_4, so库名称为camera.v4l2_test
    // 如果这个属性值为空的话,会去判断其他几个属性值,下面的代码会看到
    // 查找路径的话会依次从"/odm/lib64/hw"  "/vendor/lib64/hw"  "/system/lib64/hw"
    // 寻找,一般在"/vendor/lib64/hw"路径,下面的代码就不详细分析了,比较简单。
    snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
    if (property_get(prop_name, prop, NULL) > 0) {
    
    
        if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
    
    
            goto found;
        }
    }

    /* Loop through the configuration variants looking for a module */
    // 如果ro.hardware.camera属性没有定义,则从如下几个prop中依次查找
	// static const char *variant_keys[] = {
    
    
	//     "ro.hardware",  /* This goes first so that it can pick up a different
	//                        file on the emulator. */
	//     "ro.product.board",
	//     "ro.board.platform",
	//     "ro.arch"
	// };
    for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {
    
    
        if (property_get(variant_keys[i], prop, NULL) == 0) {
    
    
            continue;
        }
        if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
    
    
            goto found;
        }
    }

    /* Nothing found, try the default */
    // 如果以上几个属性均为定义或者对应so库未找到,则判断是否存在camera.default.so是否存在
    if (hw_module_exists(path, sizeof(path), name, "default") == 0) {
    
    
        goto found;
    }

    return -ENOENT;

found:
    /* load the module, if this fails, we're doomed, and we should not try
     * to load a different variant. */
    // 找到对应的so库后,
    return load(class_id, path, module);
}

// 我们接着分析上面找到so库后的load函数,这个函数很重要
/**
 * Load the file defined by the variant and if successful
 * return the dlopen handle and the hmi.
 * @return 0 = success, !0 = failure.
 */
 // 别忘了参数值:
 // id: camera
 // path: 上面找到的路径,假设为 /vendor/lib64/hw/
static int load(const char *id,
        const char *path,
        const struct hw_module_t **pHmi)
{
    
    
    int status = -EINVAL;
    void *handle = NULL;
    struct hw_module_t *hmi = NULL;
#ifdef __ANDROID_VNDK__
    const bool try_system = false;
#else
    const bool try_system = true;
#endif

    /*
     * load the symbols resolving undefined symbols before
     * dlopen returns. Since RTLD_GLOBAL is not or'd in with
     * RTLD_NOW the external symbols will not be global
     */
    if (try_system &&
        strncmp(path, HAL_LIBRARY_PATH1, strlen(HAL_LIBRARY_PATH1)) == 0) {
    
    
        /* If the library is in system partition, no need to check
         * sphal namespace. Open it with dlopen.
         */
        //如果位于"/system/lib64/hw",则使用dlopen函数
        handle = dlopen(path, RTLD_NOW);
    } else {
    
    
#if defined(__ANDROID_RECOVERY__)
		// recovery模式的话走这里
        handle = dlopen(path, RTLD_NOW);
#else
		// 非Recovery的话最终走的是这里
        handle = android_load_sphal_library(path, RTLD_NOW);
#endif
    }
    if (handle == NULL) {
    
    
        char const *err_str = dlerror();
        ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
        status = -EINVAL;
        goto done;
    }

	// 上面的无非就是load so,拿到handle,接下来就是关键之处了
    /* Get the address of the struct hal_module_info. */
    // HAL_MODULE_INFO_SYM_AS_STR的定义如下
    // #define HAL_MODULE_INFO_SYM_AS_STR  "HMI"
    // 所以sym也为 "HMI”
    // 注意这个值是固定的,是实现hw_get_module实现的一个标准
    // 厂商定义hal的时候,在so库里面必须定义一个结构体名称为“HMI”
    // 这里是调用so库的入口
    // 我们以Google的默认 camera hal so为例看下
	// 这里定义了一个类型为camera_module_t,名称为HAL_MODULE_INFO_SYM的结构体
	// 而HAL_MODULE_INFO_SYM的名称就是 “HMI”
	// 找到这里后会调用该结构体变量的.common.methods.open函数
	// 对应到这里的话就是v4l2_module_methods.open
	// 最终会调用v4l2_camera_hal::open_dev,如下
	// static hw_module_methods_t v4l2_module_methods = {
    
    
	//     .open = v4l2_camera_hal::open_dev};
	// camera_module_t HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
    
    
	//     .common =
	//         {
    
    
	//             .tag = HARDWARE_MODULE_TAG,
	//             .module_api_version = CAMERA_MODULE_API_VERSION_2_4,
	//             .hal_api_version = HARDWARE_HAL_API_VERSION,
	//             .id = CAMERA_HARDWARE_MODULE_ID,
	//             .name = "V4L2 Camera HAL v3",
	//             .author = "The Android Open Source Project",
	//             .methods = &v4l2_module_methods,
	//             .dso = nullptr,
	//             .reserved = {0},
	//         },
	//     .get_number_of_cameras = v4l2_camera_hal::get_number_of_cameras,
	//     .get_camera_info = v4l2_camera_hal::get_camera_info,
	//     .set_callbacks = v4l2_camera_hal::set_callbacks,
	//     .get_vendor_tag_ops = v4l2_camera_hal::get_vendor_tag_ops,
	//     .open_legacy = v4l2_camera_hal::open_legacy,
	//     .set_torch_mode = v4l2_camera_hal::set_torch_mode,
	//     .init = nullptr,
	//     .get_physical_camera_info = nullptr,
	//     .reserved = {nullptr, nullptr}};
    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
    hmi = (struct hw_module_t *)dlsym(handle, sym);
    if (hmi == NULL) {
    
    
        ALOGE("load: couldn't find symbol %s", sym);
        status = -EINVAL;
        goto done;
    }

    /* Check that the id matches */
    if (strcmp(id, hmi->id) != 0) {
    
    
        ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);
        status = -EINVAL;
        goto done;
    }

    hmi->dso = handle;

    /* success */
    status = 0;

    done:
    if (status != 0) {
    
    
        hmi = NULL;
        if (handle != NULL) {
    
    
            dlclose(handle);
            handle = NULL;
        }
    } else {
    
    
        ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
                id, path, hmi, handle);
    }

	// 返回hmi,这个是名称为“HMI”的结构体变量在so库中的地址,类型为hw_module_t
	// 但是结构体定义的地方却是camera_module_t类型的,那么这两者有啥关系呢
	// 通过查看定义就知道camera_module_t结构体的第一个成员就是hw_module_t common
	// 这样是为了方便直接调用.common.methods.open函数吧
    *pHmi = hmi;

    return status;
}

// 上面的.common.methods.open函数是so库调用的入口和起点,记住这个是标准定义,如果不这么定义的话,
// hw_get_module就会执行失败,无法加载so库。那么这个open函数主要实现什么功能呢,可能不同厂家实现都不一样,
// 但是有一个是必须的就是遍历camera节点,检查video能力等,我们可以看下Google的默认实现
static int open_dev(const hw_module_t* module,
                    const char* name,
                    hw_device_t** device) {
    
    
  return gCameraHAL.openDevice(module, name, device);
}

// 先看gCameraHAL的构造函数,主要做了以下几件事情
// 1. 遍历/dev/videoX节点
// 2. 尝试open节点并查询video能力
// 3. 构建V4L2Camera对象,放入mCameras
V4L2CameraHAL::V4L2CameraHAL() : mCameras(), mCallbacks(NULL) {
    
    
  HAL_LOG_ENTER();
  // Adds all available V4L2 devices.
  // List /dev nodes.
  DIR* dir = opendir("/dev");
  if (dir == NULL) {
    
    
    HAL_LOGE("Failed to open /dev");
    return;
  }
  // Find /dev/video* nodes.
  dirent* ent;
  std::vector<std::string> nodes;
  // 遍历/dev/video*节点
  while ((ent = readdir(dir))) {
    
    
    std::string desired = "video";
    size_t len = desired.size();
    if (strncmp(desired.c_str(), ent->d_name, len) == 0) {
    
    
      if (strlen(ent->d_name) > len && isdigit(ent->d_name[len])) {
    
    
        // ent is a numbered video node.
        nodes.push_back(std::string("/dev/") + ent->d_name);
        HAL_LOGV("Found video node %s.", nodes.back().c_str());
      }
    }
  }
  // Test each for V4L2 support and uniqueness.
  std::unordered_set<std::string> buses;
  std::string bus;
  v4l2_capability cap;
  int fd;
  int id = 0;
  for (const auto& node : nodes) {
    
    
    // Open the node.
    // 打开节点,拿到fd,用于后面ioctl
    fd = TEMP_FAILURE_RETRY(open(node.c_str(), O_RDWR));
    if (fd < 0) {
    
    
      HAL_LOGE("failed to open %s (%s).", node.c_str(), strerror(errno));
      continue;
    }
    // Read V4L2 capabilities.
    // 查询是否可以作为Camera设备使用
    if (TEMP_FAILURE_RETRY(ioctl(fd, VIDIOC_QUERYCAP, &cap)) != 0) {
    
    
      HAL_LOGE(
          "VIDIOC_QUERYCAP on %s fail: %s.", node.c_str(), strerror(errno));
    } else if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
    
    
      HAL_LOGE("%s is not a V4L2 video capture device.", node.c_str());
    } else {
    
    
      // If the node is unique, add a camera for it.
      bus = reinterpret_cast<char*>(cap.bus_info);
      if (buses.insert(bus).second) {
    
    
        HAL_LOGV("Found unique bus at %s.", node.c_str());
        // 构建V4l2Camera对象用于后续作用
        std::unique_ptr<V4L2Camera> cam(V4L2Camera::NewV4L2Camera(id++, node));
        if (cam) {
    
    
          mCameras.push_back(std::move(cam));
        } else {
    
    
          HAL_LOGE("Failed to initialize camera at %s.", node.c_str());
        }
      }
    }
    close(fd);
  }
}

好的,本节主要讲了hw_get_module的函数实现,Camera HAL的启动流程我们就先讲到这里,当然还没结束,具体So库的地方也是简单介绍了一下。后面讲CameraServer进程启动流程的时候还会涉及到CameraProvider进程,到时候我们继续。对于具体厂商So库的实现,我们只把关键的地方介绍一下(以Google默认实现为例),不会做深入的分析,后续有机会的话会把几个大平台的Camera HAL实现大概介绍一下(以网上公开资料讲解),或者尝试自己去写一个完整的Camera HAL实现,这就是后话了。

猜你喜欢

转载自blog.csdn.net/weixin_41678668/article/details/130997399