前言
在openharmony系统中,camera-HDF有逻辑ID和物理ID的概念。
逻辑ID:对上报给框架层并最终报给应用层,UI能识别调用的逻辑设备ID。
最终cameraUI会以逻辑设备ID为操作粒度。
物理ID:对下与硬件打交道的ID。
每新增一个可用设备节点就会对应一个hardwareName。
1个物理ID有可能对应几个不同用途的硬件,目前只用到了sensor硬件。
因此常规情况下的映射关系:
设备节点(/dev/video) -> hardwareName-> 物理ID(CAMERA_FIRST) ->逻辑ID(lcam001)
比如板载设备就会提前映射这种关系。
左边那个是物理ID,右边是hardwareName,最终通过hardwareName找到设备节点 。
上章提到过有个HosV4L2Dev::deviceMatch表,就是负责检测并映射hardwareName和设备节点的。
那么物理ID和逻辑ID如何映射?在HCS文件(camera_host_config.hcs)中
ability_01 :: ability {
logicCameraId = "lcam001";
physicsCameraIds = [
"CAMERA_FIRST"
];
在camera-HDF初始化时,CameraHostImpl::Init会调用CameraHostConfig::GetInstance。
CameraHostConfig *CameraHostConfig::GetInstance()
{
if (instance_ == nullptr) {
instance_ = new (std::nothrow) CameraHostConfig();
if (instance_ != nullptr) {
instance_->ReadConfigFile();
}
ReadConfigFile就会读取camera_host_config.hcb(由camera_host_config.hcs生成)。
最终存储在cameraIdMap_表中,这个表就是逻辑ID与物理ID映射表。
当框架层查询时会上报此表中的所有逻辑ID,最终上层能获取到的也是逻辑ID。后续UI界面就是对这些逻辑ID来进行操作,最终一步步的映射成设备节点的操作到内核驱动层。
上面是板载的预制场景,本章就是介绍USB设备插入后如何建立这些映射的。
UvcCallBack
上章提到,当从硬件中读取到需要的信息后,会通过回调函数传递到V4L2DeviceManager。
void V4L2DeviceManager::UvcCallBack(const std::string hardwareName, std::vector<DeviceControl>& deviceControl,
std::vector<DeviceFormat>& deviceFormat, bool uvcState)
{
//生成新的物理ID
CameraId id = ReturnEnableCameraId("");
//创建SENSOR的Controller
RetCode rc = GetManager(DM_M_SENSOR)->CreateController(DM_C_SENSOR, hardwareName);
//添加到物理ID-驱动name映射表
HardwareConfiguration hardware;
hardware.cameraId = id;
hardware.managerId = DM_M_SENSOR;
hardware.controllerId = DM_C_SENSOR;
hardware.hardwareName = hardwareName;
hardwareList_.push_back(hardware);
//创建meta数据
std::shared_ptr<CameraMetadata> meta = std::make_shared<CameraMetadata>(ITEM_CAPACITY_SIZE,
DATA_CAPACITY_SIZE);
meta->addEntry(OHOS_SENSOR_INFO_PHYSICAL_SIZE, physicalSize.data(), physicalSize.size());
Convert(deviceControl, deviceFormat, meta);
CHECK_IF_PTR_NULL_RETURN_VOID(uvcCb_);
//回调
uvcCb_(meta, uvcState, id);
生成物理ID
从CAMERA_FIRST开始往CAMERA_MAX找,如果在hardwareList_表中就表示被板载或者其他USB camera设备占了,最终找到一个没有被使用的物理ID。
hardwareList_表不仅存储了物理ID与hardwareName的映射关系,还有器件类型的ID。目前只使用了sensor,另外还有isp和flash。(当前业务没用到后面研究)
同时还根据驱动name创建了1个controller,后续对设备节点的操作都是通过这个controller来操作的。
加入hardwareList_
V4L2DeviceManager::Init初始化时会根据project_hardware.h文件生成一个表hardwareList_。之后根据这个表创建3个manager对象,用于管理3种器件。
RetCode V4L2DeviceManager::CreateManager()
{
RetCode rc = RC_OK;
std::shared_ptr<IManager> manager = nullptr;
for (auto iter = hardwareList_.cbegin(); iter != hardwareList_.cend(); iter++) {
if (CheckManagerList((*iter).managerId) == false) {
switch ((*iter).managerId) {
case DM_M_SENSOR:
manager = std::make_shared<SensorManager>(DM_M_SENSOR);
CHECK_IF_PTR_NULL_RETURN_VALUE(manager, RC_ERROR);
rc = CreateController((*iter).cameraId, manager, DM_M_SENSOR);
break;
case DM_M_FLASH:
manager = std::make_shared<FlashManager>(DM_M_FLASH);
CHECK_IF_PTR_NULL_RETURN_VALUE(manager, RC_ERROR);
rc = CreateController((*iter).cameraId, manager, DM_M_FLASH);
break;
case DM_M_ISP:
manager = std::make_shared<IspManager>(DM_M_ISP);
CHECK_IF_PTR_NULL_RETURN_VALUE(manager, RC_ERROR);
rc = CreateController((*iter).cameraId, manager, DM_M_ISP);
break;
·每个器件的manager创建后还会调用CreateController创建相应的控制器
RetCode V4L2DeviceManager::CreateController(CameraId cameraId, std::shared_ptr<IManager> manager, ManagerId managerId)
{
RetCode rc = RC_OK;
(void)cameraId;
for (auto iter = hardwareList_.cbegin(); iter != hardwareList_.cend(); iter++) {
if ((*iter).managerId == managerId) {
switch (managerId) {
case DM_M_SENSOR:
rc = manager->CreateController((*iter).controllerId, (*iter).hardwareName);
break;
case DM_M_FLASH:
rc = manager->CreateController((*iter).controllerId, (*iter).hardwareName);
break;
case DM_M_ISP:
rc = manager->CreateController((*iter).controllerId, (*iter).hardwareName);
初始化时创建的Controller其实是板载预置的,也就是之前介绍过的project_hardware.h文件。
而热插拔的USB设备节点也需要创建1个Controller,就是在生成物理ID后,通过传递过来的hardwareName创建的。
void V4L2DeviceManager::UvcCallBack(const std::string hardwareName, std::vector<DeviceControl>& deviceControl,
std::vector<DeviceFormat>& deviceFormat, bool uvcState)
{
if (uvcState) {
CAMERA_LOGI("V4L2DeviceManager uvc plug in %{public}s begin", hardwareName.c_str());
CameraId id = ReturnEnableCameraId("");
CHECK_IF_EQUAL_RETURN_VOID(id, CAMERA_MAX);
RetCode rc = GetManager(DM_M_SENSOR)->CreateController(DM_C_SENSOR, hardwareName);
最终将相关信息加入hardwareList_表。
hardware.cameraId = id;
hardware.managerId = DM_M_SENSOR;
hardware.controllerId = DM_C_SENSOR;
hardware.hardwareName = hardwareName;
hardwareList_.push_back(hardware);
CameraMetadata
每个camera设备会设置一些属性,比如设备连接类型(是板载还是USB的)、分辨率、帧率等等。板载设备是通过HDF层的设备树预置的,也是之前介绍过的camera_host_config.hcs。
USB设备没有预置的,因此需要自动生成一个。
std::shared_ptr<CameraMetadata> meta = std::make_shared<CameraMetadata>(ITEM_CAPACITY_SIZE,
DATA_CAPACITY_SIZE);
至于数据如何填充,目前我的方案就是在HCS文件中建立一个USB模板,每次新增设备就拷贝一个用于当前设备的实例,然后将从设备种查询并传递过来的控制信息、视频格式写入到metadata实例。
通过硬件设备填充数据就不说了,通过模板数据填充其他域就往上到了hostService模块了。
HostService模块
hdi_impl:camera_host_service_1.0这个模块就是cameraHDF对上提供服务的模块。
hdf_config\uhdf\device_info.hcs
hdi_server :: host {
hostName = "camera_host";
priority = 50;
gid = ["camera_host", "uhdf_driver", "vendor_mpp_driver"];
camera_device :: device {
device0 :: deviceNode {
policy = 2;
priority = 100;
moduleName = "libcamera_host_service_1.0.z.so";
serviceName = "camera_service";
框架层获取camera_service时,获取到的就是CameraHostImpl的远程调用。
上面提过CameraHostImpl初始化时会调用CameraHostConfig::GetInstance从HCS文件中加载板载camera的信息(逻辑物理ID映射关系和metadata)。除此之外还会注册一个热插拔回调
deviceManager->SetHotplugDevCallBack([this](const std::shared_ptr<CameraAbility> &meta,
const bool &status, const CameraId &cameraId) {
CameraStatus cameraStatus = status ? AVAILABLE : UN_AVAILABLE;
OnCameraStatus(cameraId, cameraStatus, meta);
这里的deviceManager就是V4L2DeviceManager。V4L2DeviceManager在UvcCallBack中最后的
回调uvcCb_就到了CameraHostImpl的OnCameraStatus。
std::string logicalCameraId = config->ReturnEnableLogicalCameraId();
config->AddUsbCameraId(logicalCameraId);
RetCode rc = config->AddCameraId(logicalCameraId, physicalCameraIds, ability);
if (rc == RC_OK && logicalCameraId.size() > 0) {
CAMERA_LOGI("add physicalCameraIds %{public}d logicalCameraId %{public}s",
static_cast<int>(cameraId), logicalCameraId.c_str());
if (cameraHostCallback_ != nullptr) {
cameraHostCallback_->OnCameraStatus(logicalCameraId, status);
}
}
std::shared_ptr<CameraDeviceImpl> cameraDevice =
CameraDeviceImpl::CreateCameraDevice(logicalCameraId);
if (cameraDevice != nullptr) {
cameraDeviceMap_[logicalCameraId] = cameraDevice;
}
第一步就是生成最新的逻辑ID,可以看到都是以lcam00开头的
std::string CameraHostConfig::ReturnEnableLogicalCameraId()
{
for (int32_t id = 1; id < CAMERA_MAX+1; id++) {
logicalCameraId = "lcam00";
logicalCameraId = logicalCameraId + std::to_string(id);
}
所以板载预置也都用lcam00x的逻辑ID,保持队列,分析问题也方便。
第二步就是将逻辑ID加入USB表中,因为后续创建流时板载走的节点和usb的不同需要区分出来。
void CameraHostConfig::AddUsbCameraId(std::string cameraId)
{
usbCameraIdVector_.push_back(cameraId);
}
然后将逻辑ID和物理ID映射加入cameraIdMap_表。
RetCode CameraHostConfig::AddCameraId(const std::string &logicalCameraId,
const std::vector<std::string> &physicalCameraIds, const std::shared_ptr<CameraAbility> ability)
{
...
if(GetCameraAbility("lcamusb", abilitytemplate) == RC_OK){
abilityNew = std::make_shared<CameraMetadata>(ITEM_CAPACITY_SIZE, DATA_CAPACITY_SIZE);
CopyCameraMetadataItems(abilityNew->get(), abilitytemplate->get());
mergMeta(abilityNew->get(), ability->get());
}
cameraIdMap_.insert(std::make_pair(logicalCameraId, physicalCameraIds));
cameraAbilityMap_.insert(std::make_pair(logicalCameraId, abilityNew!=NULL?abilityNew:ability));
...
}
这里注意了,我之前提过的metadata数据逻辑在这。
先把HCS中预置的usb模板metadata数据拷贝一份,然后将我们之前从硬件获取的meta数据合并过去,这样就形成了一份此硬件对应的metadata数据并加入cameraAbilityMap_表。
最后针对这个逻辑ID创建一个CameraDeviceImpl实例放入cameraDeviceMap_表中。
总结
上述流程主要是生成了一个物理ID和逻辑ID并把对应关系加入表中方便后续查询。
在devicemanager层创建了一个sensorcontroller(对于hardwarename)便于后续操作设备文件。
在hostservice层创建了一个CameraDeviceImpl(对应逻辑ID)给上层设备调用提供实例。
最后还自动为此设备填充了metadata数据作为参数以便后续使用。
至此在HDF-camera中USB设备插入初始化流程结束。