ION 的前任是 PMEM,ION,最显著的特点是它可以被用户空间的进程之间或者内核空间的模块之间进行内存共享,
而且这种共享可以是零拷贝的。在实际使用中,ION 和 VIDEOBUF2、DMA-BUF、V4L2 等结合的很紧密。
ION在内核中被当做一个misc设备来注册,通常user通过打开/dev/ion来对内存进行操作,在高通710平台的camera流
程中,通过ion拿取camera sensor参数.
怎么用的?
cam_sensor_subdev_ioctl过程(在vendor层通过调用media->ioctl获取与camera相关的节点后,打开v4l2-subdev部分)
hal的传递
HwEnvironment::Initialize -> HwEnvironment::GetInstance -> HwEnvironment::InitCaps ->
HwEnvironment::ProbeImageSensorModules -> result = pData->Probe(&detected, &deviceIndex); ->
ImageSensorModuleData::Probe //详细见之前的博客 https://blog.csdn.net/linken_yue/article/details/124756037?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22124756037%22%2C%22source%22%3A%22linken_yue%22%7D&ctrtid=kc0L6
//camx\src\core\camximagesensormoduledata.cpp
CamxResult ImageSensorModuleData::Probe(
BOOL* pDetected,
INT32* pDeviceIndex)
{
pCmdBegin = pSlaveInfoCmd->BeginCommands(I2CInfoCmdSize / sizeof(UINT32)); //获取buffer的起始地址
if (NULL != pCmdBegin)
{
CSLSensorI2CInfo* pCmdI2CInfo = reinterpret_cast<CSLSensorI2CInfo*>(pCmdBegin);
result = GetSensorDataObject()->CreateI2CInfoCmd(pCmdI2CInfo); //从sensor_module_XXX.xml取对应信息
if (CamxResultSuccess == result)
{
result = pSlaveInfoCmd->CommitCommands(); //存放回buffer中
}
}
。。。。。。 //类似以上获取其他信息
result = CSLImageSensorProbe(pProbePacket->GetMemHandle(), pProbePacket->GetOffset(), &probeResult);
}
//camx\src\core\camxcsl.cpp
CamxResult CSLImageSensorProbe(
CSLMemHandle hPacket,
SIZE_T offset,
CSLImageSensorProbeResult* pProbeResult)
{
CAMX_ENTRYEXIT_SCOPE(CamxLogGroupCSL, CamX::SCOPEEventCSLImageSensorProbe);
CAMX_ASSERT_MESSAGE(NULL != g_pCSLModeManager, "CSL not initialized");
CSLJumpTable* pJumpTable = g_pCSLModeManager->GetJumpTable();
return pJumpTable->CSLImageSensorProbe(hPacket, offset, pProbeResult);
}
//camx\src\core\camxcslhw.cpp
CamxResult CSLImageSensorProbeHW(
CSLMemHandle hPacket,
SIZE_T offset,
CSLImageSensorProbeResult* pProbeResult)
{
result = CSLHwInternalProbeSensorHW(hPacket, offset, &deviceIndex);
}
//camx\src\core\camxcslhwinternalsensor.cpp
CamxResult CSLHwInternalProbeSensorHW(
CSLMemHandle hPacket,
SIZE_T offset,
INT32* pDeviceIndex)
{
ioctlCmd.op_code = CAM_SENSOR_PROBE_CMD;
ioctlCmd.size = sizeof(ioctlCmd.handle);
ioctlCmd.handle_type = CAM_HANDLE_MEM_HANDLE;
ioctlCmd.reserved = 0;
ioctlCmd.handle = hPacket;
result = pLoophw->deviceOp.Ioctl(pLoophw, VIDIOC_CAM_CONTROL, &ioctlCmd);
}
kenrel的传递
我们从 cam_sensor_subdev_ioctl()函数开始:
/*1*/cam_sensor_subdev_ioctl(){
.....
struct cam_sensor_ctrl_t *s_ctrl =
v4l2_get_subdevdata(sd);
switch (cmd) {
case VIDIOC_CAM_CONTROL:
rc = cam_sensor_driver_cmd(s_ctrl, arg);/* 主要看这个函数 */
break;
}
return rc;
.....
}
//techpack\camera\drivers\cam_sensor_module\cam_sensor\cam_sensor_core.c
cam_sensor_driver_cmd(s_ctrl, arg);{
.....
/* pu & pd 是关于power的变量,这里还是空的,下面省略部分会分配内存*/
struct cam_sensor_power_setting *pu = NULL;
struct cam_sensor_power_setting *pd = NULL;
struct cam_sensor_power_ctrl_t *power_info = /* 这里把power_info指向 sctrl中的power_info(现在是空的) */
&s_ctrl->sensordata->power_info;
.....
/* 这里把power_info中的参数指向 pu,pd; 后面ion中拿出来的power数据会填写到power_info中,由于power_info已经指向
pu,pd所以s_ctrl->sensordata->power_info = pu,pd = power_info,所以在后面对s_ctrl操作,就是对power_info操作 */
power_info->power_setting = pu;
power_info->power_down_setting = pd;
if (cmd->handle_type ==
CAM_HANDLE_MEM_HANDLE) {
rc = cam_handle_mem_ptr(cmd->handle, s_ctrl); /* 重点,这里开始从ion拿数据了 */
if (rc < 0) {
CAM_ERR(CAM_SENSOR, "Get Buffer Handle Failed");
kfree(pu);
kfree(pd);
goto release_mutex;
}
} else {
CAM_ERR(CAM_SENSOR, "Invalid Command Type: %d",
cmd->handle_type);
return -EINVAL;
}
.....
}
//techpack\camera\drivers\cam_sensor_module\cam_sensor\cam_sensor_core.c
cam_handle_mem_ptr(cmd->handle, s_ctrl);{
.....
rc = cam_mem_get_cpu_buf(handle, /* 通过handle,拿到所需数据在ion中的地址packet,以及len */
(uint64_t *)&packet, &len);
.....
/* 这里高通自己的计算方法,计算后应该是得到 command descriptor(控制标识之类的东西), 在后面拿数据时会用 */
/* 具体操作看 cam_mem_get_cpu_buf详解 */
pkt = (struct cam_packet *)packet;
cmd_desc = (struct cam_cmd_buf_desc *)
((uint32_t *)&pkt->payload + pkt->cmd_buf_offset/4);
.....
for (i = 0; i < pkt->num_cmd_buf; i++) { /* 循环拿buf数据,比如power数据,salve_info,iic */
if (!(cmd_desc[i].length))
continue;
/* 通过handle(可以理解为标识)拿我们需要的数据放到 cmd_buf1中 */
rc = cam_mem_get_cpu_buf(cmd_desc[i].mem_handle,
(uint64_t *)&cmd_buf1, &len);
if (rc < 0) {
CAM_ERR(CAM_SENSOR,
"Failed to parse the command Buffer Header");
return -EINVAL;
}
/* 一系列计算(地址偏移)后,把ptr指向 cmd_buf 既指向cmd_buf1 */
cmd_buf = (uint32_t *)cmd_buf1;
cmd_buf += cmd_desc[i].offset/4;
ptr = (void *) cmd_buf;
/* 从ptr中拿取数据放到 s_ctrl,比如power_setting salve_info等*/
rc = cam_handle_cmd_buffers_for_probe(ptr, s_ctrl,
i, cmd_desc[i].length);
if (rc < 0) {
CAM_ERR(CAM_SENSOR,
"Failed to parse the command Buffer Header");
return -EINVAL;
}
}
}
//techpack\camera\drivers\cam_sensor_module\cam_sensor\cam_sensor_core.c
cam_handle_cmd_buffers_for_probe(ptr, s_ctrl, i, cmd_desc[i].length);
{
int32_t rc = 0;
switch (cmd_buf_num) { /* 在上一个函数中拿的是什么数据,就会case到几 */
case 0: { /* 关于isalve_info,以及i2c的信息,放到sctrl中,之前已经说过为什么放到s_ctrl中 */
struct cam_cmd_i2c_info *i2c_info = NULL;
struct cam_cmd_probe *probe_info;
i2c_info = (struct cam_cmd_i2c_info *)cmd_buf;
rc = cam_sensor_update_i2c_info(i2c_info, s_ctrl);
if (rc < 0) {
CAM_ERR(CAM_SENSOR, "Failed in Updating the i2c Info");
return rc;
}
probe_info = (struct cam_cmd_probe *)
(cmd_buf + sizeof(struct cam_cmd_i2c_info));
rc = cam_sensor_update_slave_info(probe_info, s_ctrl);
if (rc < 0) {
CAM_ERR(CAM_SENSOR, "Updating the slave Info");
return rc;
}
cmd_buf = probe_info;
}
break;
case 1: {/* 关于power_setting的数据 */
rc = cam_sensor_update_power_settings(cmd_buf,
cmd_buf_length, &s_ctrl->sensordata->power_info);
if (rc < 0) {
CAM_ERR(CAM_SENSOR,
"Failed in updating power settings");
return rc;
}
}
break;
default:
CAM_ERR(CAM_SENSOR, "Invalid command buffer");
break;
}
return rc;
}
cam_mem_get_cpu_buf详解
ion的alloc和map可以参考下面文章
https://blog.csdn.net/a185531353/article/details/89479076
int cam_mem_get_cpu_buf(int32_t buf_handle, uint64_t *vaddr_ptr, size_t *len)
{
int rc = 0;
int idx;
struct ion_handle *ion_hdl = NULL;
uint64_t kvaddr = 0;
size_t klen = 0;
if (!buf_handle || !vaddr_ptr || !len)
return -EINVAL;
/* 通过之前拿到的buf_handle
拿到idx(追代码时发现其实际上是一个句柄),通过idx可以找到ion_hdl */
idx = CAM_MEM_MGR_GET_HDL_IDX(buf_handle);
if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0)
return -EINVAL;
if (!tbl.bufq[idx].active)
return -EPERM;
mutex_lock(&tbl.bufq[idx].q_lock);
if (buf_handle != tbl.bufq[idx].buf_handle) {
rc = -EINVAL;
goto exit_func;
}
/* 通过idx拿到ION_hdl */
ion_hdl = tbl.bufq[idx].i_hdl;
if (!ion_hdl) {
CAM_ERR(CAM_CRM, "Invalid ION handle");
rc = -EINVAL;
goto exit_func;
}
if (tbl.bufq[idx].flags & CAM_MEM_FLAG_KMD_ACCESS) {
if (!tbl.bufq[idx].kmdvaddr) {
/* 这里拿到的kvaddr才是真正的sensor参数存放在ion中的地址 */
rc = cam_mem_util_map_cpu_va(ion_hdl,
&kvaddr, &klen);
if (rc)
goto exit_func;
tbl.bufq[idx].kmdvaddr = kvaddr; /* 赋值 */
}
} else {
rc = -EINVAL;
goto exit_func;
}
/* 赋值,这里的vaddr_ptr就是之前传进来的cmd_buf1 */
*vaddr_ptr = tbl.bufq[idx].kmdvaddr;
*len = tbl.bufq[idx].len;
exit_func:
mutex_unlock(&tbl.bufq[idx].q_lock);
return rc;
}