【qcom camx】手电筒打开流程

手机手电筒打开,cam-kernel流程

  1. CAM_ACQUIRE_DEV: 获取dev
    在这里插入图片描述

  2. CAM_CONFIG_DEV: 接受init-setting
    在这里插入图片描述

  3. CAM_CONFIG_DEV: 接受stream-off setting

  4. CAM_START_DEV:stream on

  5. CAM_CONFIG_DEV:Torch on动作走NRT-NoRealTime
    在这里插入图片描述

手机手电筒关闭,cam-kernel流程

  1. CAM_CONFIG_DEV: Torch off动作走NRT-NoRealTime
  2. CAM_START_DEV:stream off
  3. CAM_RELEASE_DEV:Destroy

下拉菜单手电筒是systemUi下面的一个功能,调用的是camera接口

     public void setFlashlight(final boolean enabled) {
          if (mForceOff) {
              Slog.d(TAG, "setFlashlight: force off state");
              postShowToast();
              return;
          }
          if (Constants.SUPPORT_ANDROID_FLASHLIGHT) {
              int failedCount = 0;
              while (mCameraId == null && failedCount < 2) {
                  initCameraFlash();
                  failedCount++;
              }
              if (mCameraId != null) {
                  mBgHandler.post(new Runnable() {
                      @Override
                      public void run() {
                          setNormalFlashlight(enabled);
                      }
                  });
              } else {
                  Slog.d(TAG, "setFlashlight: enabled: " + enabled + ", could not initialize cameraId");
              }
          } else {
              setMiuiFlashlight(enabled);
          }
      }
  
      private void setNormalFlashlight(boolean enabled) {
          boolean pendingError = false;
          synchronized (this) {
              if (!mTorchAvailable) {
                  Slog.d(TAG, "setNormalFlashlight: enabled: " + enabled + ", torchAvailable: " + mTorchAvailable);
                  return;
              }
              if (mFlashlightEnabled != enabled) {
                  mFlashlightEnabled = enabled;
                  try {
                      mCameraManager.setTorchMode(mCameraId, enabled);
                  } catch (CameraAccessException e) {
                      Log.e(TAG, "Couldn't set torch mode", e);
                      mFlashlightEnabled = false;
                      pendingError = true;
                  }
              }
          }
          if (pendingError) {
              dispatchError();
          } else {
              dispatchModeChanged(enabled);
          }
      }
  
      private void setMiuiFlashlight(boolean enabled) {
          if (setMiuiFlashModeInternal(enabled)) {
              mFlashlightEnabled = enabled;
              dispatchModeChanged(enabled);
          }
      }

通过hal3接口set_torch_mode()接口,调用,先进行初始化,然后再进行操作。对于camera-kernel来说就行先acquire 然后config 接着就是start dev,具体流程如下:

camxhal3entry.cpp -> camxhal3.cpp: pHAL3->set_torch_mode()
camxhal3.cpp -> chxextensioninterface.cpp: GetCHIAppCallbacks()->chi_remap_camera_id
chxextensioninterface.cpp -> chxextensionmodule.cpp :pExtensionModule->GetCameraInfo()
chxextensionmodule.cpp -> chxextensionmodule.cpp: ExtensionModule::GetCameraInfo()
chxextensionmodule.cpp --> chxextensioninterface.cpp
chxextensioninterface.cpp --> camxhal3.cpp
note right of camxhal3.cpp:BOOL enableTorch = (TRUE == enabledAPI) ? TRUE : FALSE;
camxhal3.cpp -> camxhal3module.cpp: HAL3Module::GetInstance()->SetTorchMode()
note left of camxhal3module.cpp:TorchModeStatus torchStatus = (TRUE == enableTorch) ? TorchModeStatusAvailableOn : TorchModeStatusAvailableOff;
camxhal3module.cpp -> camxhal3module.cpp:SetTorchModeInternal()
note left of camxhal3module.cpp:case TorchModeStatusAvailableOn:
camxhal3module.cpp -> camxhal3module.cpp: InitConfigurationForTorch()
camxhal3module.cpp -> camxhal3module.cpp: ProcessCameraOpen();
camxhal3module.cpp-> chxextensioninterface.cpp: m_ChiAppCallbacks.chi_extend_open();
chxextensioninterface.cpp -> chxextensionmodule.cpp: pExtensionModule->ExtendOpen();
chxextensioninterface.cpp <-- chxextensionmodule.cpp
chxextensioninterface.cpp --> camxhal3module.cpp
camxhal3module.cpp -> chxextensioninterface.cpp: pCHIAppCallbacks->chi_initialize_override_session()
chxextensioninterface.cpp -> chxextensionmodule.cpp: InitializeOverrideSession()
chxextensionmodule.cpp -> chxextensionmodule.cpp: GetSelectedResolutionForActiveSensorMode()
chxextensionmodule.cpp -> chxusecaseutils.cpp: UsecaseSelector::GetSensorModeInfo()
chxusecaseutils.cpp -> chxsensorselectmode.cpp: ChxSensorModeSelect::FindBestSensorMode()
chxsensorselectmode.cpp --> chxusecaseutils.cpp
chxusecaseutils.cpp --> chxextensionmodule.cpp
chxextensionmodule.cpp -> chxusecaseutils.cpp:m_pUsecaseSelector->GetMatchingUsecase()
chxusecaseutils.cpp -> chxusecaseutils.cpp:IsTorchWidgetUsecase()
note right of chxusecaseutils.cpp:usecaseId = UsecaseId::Torch;
chxusecaseutils.cpp --> chxextensionmodule.cpp
chxextensionmodule.cpp -> chxusecaseutils.cpp: m_pUsecaseFactory->CreateUsecaseObject()
chxusecaseutils.cpp -> chxusecasetorch.cpp: UsecaseTorch::Create()
note right of chxusecasetorch.cpp: create pipeline for torch usecase
chxusecasetorch.cpp -> chxusecasetorch.cpp: pUsecaseTorch->Initialize()
chxusecasetorch.cpp -> chxpipeline.cpp: Pipeline::Create()
chxpipeline.cpp -> chxpipeline.cpp: pPipeline->Initialize()
chxpipeline.cpp --> chxusecasetorch.cpp: return pPipeline
chxusecasetorch.cpp -> chxpipeline.cpp:m_pTorchPipeline->CreateDescriptor()
chxpipeline.cpp -> chxextensionmodule.cpp: ExtensionModule::GetInstance()->CreatePipelineDescriptor(&pipelineCreateData)
note left of camxchi.cpp: pCreatePipelineDescriptor = CamX::ChiCreatePipelineDescriptor;
chxextensionmodule.cpp -> camxchi.cpp: g_chiContextOps.pCreatePipelineDescriptor()
camxchi.cpp -> camxchicontext.cpp: pChiContext->CreatePipelineDescriptor()
note left of camxchicontext.cpp:Unfortunately,
 we don't know the lifetime of the objects being pointed to, 
 so we have to assume they will not\nexist after this function call,
 and certainly not by the call to CamX::Session::Initialize, so we mightas well perform the conversion here, 
 and keep the data in the format we expect
camxchicontext.cpp -> camxchicontext.cpp: ProcessPipelineCreateDesc()
camxchicontext.cpp -> camxchicontext.cpp: SetPipelineDescriptorOutput()
camxchicontext.cpp -> camxpipeline.cpp: Pipeline::Create()
camxpipeline.cpp -> camxpipeline.cpp: pPipeline->Initialize()
camxpipeline.cpp -> camxpipeline.cpp: CreateNodes()
camxpipeline.cpp -> camxhwcontext.cpp: m_pChiContext->GetHwContext()->GetImageSensorModuleData()
camxhwcontext.cpp -> camxhwenvironment.cpp:m_pHwEnvironment->GetImageSensorModuleData();
camxhwcontext.cpp <-- camxhwenvironment.cpp
camxhwcontext.cpp --> camxpipeline.cpp
camxpipeline.cpp -> camxnode.cpp: Node::Create(&createInputData, &createOutputData);
camxnode.cpp -> camxnode.cpp: HwEnvironment::GetInstance()->GetHwFactory();
camxnode.cpp -> camxhwfactory.cpp: pFactory->CreateNode();
camxhwfactory.cpp -> camxtitan17xfactory.cpp: HwCreateNode();
camxtitan17xfactory.cpp -> camxtorchnode.cpp: TorchNode::Create();
camxtorchnode.cpp --> camxtitan17xfactory.cpp: return CAMX_NEW TorchNode;
camxtitan17xfactory.cpp --> camxhwfactory.cpp
camxhwfactory.cpp --> camxnode.cpp
camxnode.cpp -> camxnode.cpp: pNode->Initialize();
camxnode.cpp -> camxtorchnode.cpp: ProcessingNodeInitialize();
camxtorchnode.cpp --> camxnode.cpp
camxnode.cpp --> camxpipeline.cpp
camxpipeline.cpp --> camxchicontext.cpp
camxchicontext.cpp --> camxchi.cpp
camxchi.cpp --> chxextensionmodule.cpp
chxextensionmodule.cpp --> chxpipeline.cpp
chxpipeline.cpp --> chxusecasetorch.cpp
chxusecasetorch.cpp -> chxsession.cpp: Session::Create(&m_pTorchPipeline, 1, &callbacks, &m_torchSessionPvtData);
chxsession.cpp -> chxsession.cpp: pSession->Initialize();
chxsession.cpp -> chxextensionmodule.cpp: ExtensionModule::GetInstance()->CreateSession(&sessionCreateData);
note right of chxextensionmodule.cpp:pChiContextOps->pCreateSession             = CamX::ChiCreateSession;
chxextensionmodule.cpp -> camxchi.cpp: g_chiContextOps.pCreateSession()
camxchi.cpp -> camxchicontext.cpp: pChiContext->CreateSession()
camxchicontext.cpp -> camxchisession.cpp: CHISession::Create(&createData);
camxchisession.cpp -> camxchisession.cpp: pCHISession->Initialize();
camxchisession.cpp -> camxsession.cpp: Session::Initialize();
camxsession.cpp -> camxsession.cpp: InitializeNewPipelines(pCreateData);
camxsession.cpp -> camxsession.cpp: FinalizePipeline(pCreateData,..)
camxsession.cpp -> camxpipeline.cpp: m_pipelineData[pipelineIndex].pPipeline->FinalizePipeline(&finalizeInitializationData);
camxpipeline.cpp -> camxnode.cpp: m_ppNodes[i]->FinalizeInitialization(pFinalizeInitializationData);
camxnode.cpp -> camxtorchnode.cpp: ProcessingNodeFinalizeInitialization(pFinalizeInitializationData);
camxtorchnode.cpp -> camxflash.cpp: Flash::Create(&createData)
camxflash.cpp -> camxflash.cpp: pCreateData->pFlash->Initialize();
camxflash.cpp -> camxflash.cpp: CreateInitializePacket();
camxflash.cpp -> camxflash.cpp: pPacket->SetOpcode(CSLDeviceTypeFlash, CSLPacketOpcodesFlashInitialConfig);
camxflash.cpp -> camxflash.cpp: pPacket->AddCmdBufferReference(pI2CCmdBuffer, NULL);
camxflash.cpp -> camxflash.cpp: pPacket->AddCmdBufferReference(pPowerCmdBuffer, NULL);
camxflash.cpp -> camxflash.cpp: pPacket->AddCmdBufferReference(pI2CInitCmdBuffer, NULL);
camxflash.cpp -> camxflash.cpp: pPacket->CommitPacket();
camxflash.cpp -> camxhwcontext.cpp: m_pHwContext->Submit();
camxhwcontext.cpp -> camxcsl.cpp: CSLSubmit();
camxcsl.cpp -> camxcslhw.cpp: pJumpTable->CSLSubmit();
camxcslhw.cpp -> camxcslhwinternal.cpp: pHWDevice->deviceOp.Submit()
note left of camxcslhwinternal.cpp:camxcslhwinternalflash.cppCSLHwDeviceOps g_CSLHwDeviceFlashOps ={...CSLHwInternalDefaultIoctl,CSLHwInternalDefaultSubmit,\n...\n}
camxcslhwinternal.cpp -> camxcslhwinternal.cpp: pDevice->deviceOp.Ioctl(pDevice, VIDIOC_CAM_CONTROL, &ioctlCmd);
note right of camxcslhwinternal.cpp:ioctlCmd.op_code = CAM_CONFIG_DEV;
camxcslhwinternal.cpp -> cam_flash_dev.c: ioctl(pDevice->fd, request, pArg);
note left of cam_flash_dev.c: cam_flash_subdev_ioctl()
cam_flash_dev.c -> cam_flash_dev.c:cam_flash_driver_cmd()
note right of cam_flash_dev.c: fctrl->func_tbl.parser=cam_flash_i2c_pkt_parser;
cam_flash_dev.c -> cam_flash_core.c:fctrl->func_tbl.parser();
cam_flash_core.c -> cam_flash_core.c:case CAM_FLASH_PACKET_OPCODE_INIT:
camxflash.cpp -> camxflash.cpp: CreateStreamOffPacket();
camxflash.cpp -> camxflash.cpp:pPacket->SetOpcode(CSLDeviceTypeFlash, CSLPacketOpcodesFlashStreamOff);

初始化完毕,然后就是torch on

camxhal3module.cpp -> camxhal3module.cpp: SubmitRequestForTorch();
camxhal3module.cpp-> chxextensioninterface.cpp: m_ChiAppCallbacks.chi_override_process_request();
chxextensioninterface.cpp -> chxextensionmodule.cpp: pExtensionModule->OverrideProcessRequest();
chxextensionmodule.cpp -> chxusecase.cpp: m_pSelectedUsecase[logicalCameraId]->ProcessCaptureRequest(pCaptureRequest);
chxusecase.cpp -> chxusecasetorch.cpp: ExecuteCaptureRequest(pRequest);
chxusecasetorch.cpp -> chxusecasetorch.cpp: SubmitChiRequest(pRequest);
chxusecasetorch.cpp -> chxusecase.cpp: SubmitRequest(&submitRequest);
chxusecase.cpp -> chxextensionmodule.cpp: ExtensionModule::GetInstance()->SubmitRequest(pSubmitRequestData);
note left of camxchi.cpp: pChiContextOps->pSubmitPipelineRequest     = CamX::ChiSubmitPipelineRequest;
chxextensionmodule.cpp -> camxchi.cpp: g_chiContextOps.pSubmitPipelineRequest(m_hCHIContext, pSubmitRequest);
camxchi.cpp -> camxchicontext.cpp: pChiContext->SubmitRequest();
camxchicontext.cpp -> camxsession.cpp: pSession->ProcessCaptureRequest();
camxsession.cpp -> camxsession.cpp: SyncProcessCaptureRequest();
camxsession.cpp -> camxsession.cpp: CanRequestProceed();
note left of camxchisession.cpp: m_pThreadManager->RegisterJobFamily(ThreadJobCallback,wrapperName,NULL,JobPriority::Normal,TRUE,&m_hJobFamilyHandle);\n此时已经新dispatch了一个新的线程
camxsession.cpp -> camxchisession.cpp: m_pThreadManager->PostJob(m_hJobFamilyHandle,..)
camxchisession.cpp -> camxchisession.cpp: ThreadJobCallback()
camxchisession.cpp -> camxchisession.cpp: pSession->ThreadJobExecute();
camxchisession.cpp -> camxsession.cpp: ProcessRequest();
camxsession.cpp -> camxpipeline.cpp: m_pipelineData[rRequest.pipelineIndex].pPipeline->ProcessRequest();
camxpipeline.cpp -> camxnode.cpp: m_ppOrderedNodes[nodeIndex]->SetupRequest()
camxnode.cpp -> camxnode.cpp: NewActiveStreamsSetup();
camxnode.cpp --> camxpipeline.cpp:
camxpipeline.cpp -> camxdeferredrequestqueue.cpp: m_pDeferredRequestQueue->DispatchReadyNodes();
note left of camxchisession.cpp:回调函数:DeferredWorkerWrapper()
camxdeferredrequestqueue.cpp -> camxchisession.cpp:m_pThreadManager->PostJob(m_hDeferredWorker, NULL, &pData[0], FALSE, FALSE);
camxchisession.cpp -> camxdeferredrequestqueue.cpp: DeferredWorkerWrapper()
camxdeferredrequestqueue.cpp -> camxdeferredrequestqueue.cpp: pDeferredQueue->DeferredWorkerCore();
camxdeferredrequestqueue.cpp -> camxnode.cpp: pNode->ProcessRequest();
camxnode.cpp -> camxtorchnode.cpp: ExecuteProcessRequest(&executeProcessData);
camxtorchnode.cpp -> camxflash.cpp: m_pFlash->ExecuteProcessRequest(pExecuteProcessRequestData, FlashInfoTypeMain, NULL, NULL);
note right of camxflash.cpp:Flash::ExecuteProcessRequest()
camxflash.cpp -> camxflash.cpp: HandleTorch();
camxflash.cpp -> camxflash.cpp: Fire(FlashOperation::Low,requestId, pShouldSendNop);
camxflash.cpp -> camxflash.cpp: FireI2C();
note right of camxflash.cpp: opcode = CSLPacketOpcodesFlashSetNonRealTime;
camxflash.cpp -> camxflash.cpp: pPacket->SetOpcode();
camxflash.cpp -> camxflash.cpp: pPacket->AddCmdBufferReference(pFireI2CCmdBuffer, NULL);
camxflash.cpp -> camxflash.cpp: pPacket->CommitPacket();
camxflash.cpp -> camxhwcontext.cpp: m_pHwContext->Submit();
camxhwcontext.cpp -> camxcsl.cpp: CSLSubmit();
camxcsl.cpp -> camxcslhw.cpp: pJumpTable->CSLSubmit();
camxcslhw.cpp -> camxcslhwinternal.cpp: pHWDevice->deviceOp.Submit()
note left of camxcslhwinternal.cpp:camxcslhwinternalflash.cpp\nCSLHwDeviceOps g_CSLHwDeviceFlashOps ={\n...\nCSLHwInternalDefaultIoctl,\nCSLHwInternalDefaultSubmit,\n...\n}
camxcslhwinternal.cpp -> camxcslhwinternal.cpp: pDevice->deviceOp.Ioctl(pDevice, VIDIOC_CAM_CONTROL, &ioctlCmd);
note right of camxcslhwinternal.cpp:ioctlCmd.op_code = CAM_CONFIG_DEV;
camxcslhwinternal.cpp -> cam_flash_dev.c: ioctl(pDevice->fd, request, pArg);
note left of cam_flash_dev.c: cam_flash_subdev_ioctl()
cam_flash_dev.c -> cam_flash_dev.c:cam_flash_driver_cmd()
note right of cam_flash_dev.c: fctrl->func_tbl.parser=cam_flash_i2c_pkt_parser;
cam_flash_dev.c -> cam_flash_core.c:fctrl->func_tbl.parser();
cam_flash_core.c -> cam_flash_core.c:case CAM_FLASH_PACKET_OPCODE_NON_REALTIME_SET_OPS:

关闭闪光灯的流程如下:

camxhal3entry.cpp -> camxhal3.cpp: pHAL3->set_torch_mode()
camxhal3.cpp -> chxextensioninterface.cpp: GetCHIAppCallbacks()->chi_remap_camera_id
chxextensioninterface.cpp -> chxextensionmodule.cpp :pExtensionModule->GetCameraInfo()
chxextensionmodule.cpp -> chxextensionmodule.cpp: ExtensionModule::GetCameraInfo()
chxextensionmodule.cpp --> chxextensioninterface.cpp
chxextensioninterface.cpp --> camxhal3.cpp
note right of camxhal3.cpp:BOOL enableTorch = (TRUE == enabledAPI) ? TRUE : FALSE;
camxhal3.cpp -> camxhal3module.cpp: HAL3Module::GetInstance()->SetTorchMode()
note left of camxhal3module.cpp:TorchModeStatus torchStatus = (TRUE == enableTorch) ? TorchModeStatusAvailableOn : TorchModeStatusAvailableOff;
camxhal3module.cpp -> camxhal3module.cpp:SetTorchModeInternal()
note left of camxhal3module.cpp:case TorchModeStatusAvailableOff:
camxhal3module.cpp -> camxhal3module.cpp: SubmitRequestForTorch(logicalCameraId, torchStatus);
camxhal3module.cpp -> camxhal3module.cpp: DeInitConfigurationForTorch(logicalCameraId);
camxhal3module.cpp -> camxhal3module.cpp: HAL3Module::GetInstance()->ProcessCameraClose(cameraId, &extend);
camxhal3module.cpp-> chxextensioninterface.cpp: m_ChiAppCallbacks.chi_extend_close();
chxextensioninterface.cpp -> chxextensionmodule.cpp: pExtensionModule->ExtendClose();
chxextensioninterface.cpp <-- chxextensionmodule.cpp
chxextensioninterface.cpp --> camxhal3module.cpp
camxhal3module.cpp -> chxextensioninterface.cpp: pCHIAppCallbacks->chi_teardown_override_session();
chxextensioninterface.cpp -> chxextensionmodule.cpp: TeardownOverrideSession()
chxextensionmodule.cpp -> chxextensionmodule.cpp: TeardownOverrideUsecase(camera3_device, FALSE);
chxextensionmodule.cpp -> chxusecase.cpp: pUsecase->DestroyObject(isForced);
chxusecase.cpp -> chxsession.cpp: Destroy(isForced);
chxsession.cpp -> chxextensionmodule.cpp: ExtensionModule::GetInstance()->DestroySession(m_hSession, isForced);
note left of camxchi.cpp: pChiContextOps->pDestroySession  = CamX::ChiDestroySession;
chxextensionmodule.cpp -> camxchi.cpp: g_chiContextOps.pDestroySession(m_hCHIContext, sessionHandle, isForced);
camxchi.cpp -> camxchicontext.cpp: pChiContext->DestroySession(pCHISession);
camxchicontext.cpp -> camxsession.cpp: pChiSession->Destroy();
camxsession.cpp -> camxpipeline.cpp: m_pipelineData[i].pPipeline->StreamOff(CHIDeactivateModeDefault);
camxpipeline.cpp -> camxchicontext.cpp: m_pChiContext->GetHwContext()->StreamOff();
camxchicontext.cpp -> camxcsl.cpp: CSLStreamOff()
camxcsl.cpp -> camxcslHW.cpp: pJumpTable->CSLStreamOff(hCSL, phLink, phDevices, mode);
note left of camxcslHW.cpp:CSLHwStreamOffKMDHardwares()
camxcslHW.cpp -> camxcslhwinternalflash.cpp: pHWDevice->deviceOp.StreamOff();
note left of camxcslhwinternalflash.cpp:CSLHwInternalFlashKMDStreamOff()
camxcslhwinternalflash.cpp -> camxcslhwinternal.cpp: CSLHwInternalKMDStreamOp(hCSLHwSession, hDevice, deviceIndex, FALSE);
note left of camxcslhwinternal.cpp: ioctlCmd.op_code = CAM_STOP_DEV;
camxcslhwinternal.cpp -> cam_flash_dev.c: pDevice->deviceOp.Ioctl(pDevice, VIDIOC_CAM_CONTROL, &ioctlCmd);
cam_flash_dev.c --> camxcslhwinternal.cpp
camxcslhwinternal.cpp --> camxcslhwinternalflash.cpp
camxcslhwinternalflash.cpp --> camxcslHW.cpp
camxcslHW.cpp --> camxcsl.cpp
camxcsl.cpp --> camxchicontext.cpp
camxchicontext.cpp --> camxpipeline.cpp
camxpipeline.cpp --> camxsession.cpp
camxsession.cpp -> camxdeferredrequestqueue.cpp: m_pDeferredRequestQueue->Destroy();
camxdeferredrequestqueue.cpp --> camxsession.cpp
camxsession.cpp -> camxpipeline.cpp: m_pipelineData[i].pPipeline->Destroy();
camxpipeline.cpp -> camxflash.cpp: CAMX_DELETE this;
camxflash.cpp -> camxsensornode.cpp: SensorSubDevicesCache::GetInstance()->ReleaseOneSubDevice(m_cameraId, FlashHandle);
camxsensornode.cpp ->  camxcsl.cpp: CSLReleaseDevice();
camxcsl.cpp -> camxcslHW.cpp: pJumpTable->CSLReleaseDevice(hCSL, hDevice);
note left of camxcslhwinternal.cpp: CSLHwInternalKMDRelease()
camxcslHW.cpp -> camxcslhwinternal.cpp: pHWDevice->deviceOp.Release();
note left of camxcslhwinternal.cpp: ioctlCmd.op_code = CAM_RELEASE_DEV;
camxcslhwinternal.cpp -> camxcslhwinternal.cpp: pDevice->deviceOp.Ioctl(pDevice, VIDIOC_CAM_CONTROL, &ioctlCmd);
camxcslhwinternal.cpp -> cam_flash_dev.c

在这里插入图片描述

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值