高通(QCOM)camera kernel新架构通过ION拿到sensor参数

前言:

ION 的前任是 PMEM,ION,最显著的特点是它可以被用户空间的进程之间或者内核空间的模块之间进行内存共享,
而且这种共享可以是零拷贝的。在实际使用中,ION 和 VIDEOBUF2、DMA-BUF、V4L2 等结合的很紧密。
ION在内核中被当做一个misc设备来注册,通常user通过打开/dev/ion来对内存进行操作,在高通710平台的camera流
程中,拿取camera参数与之前的660平台不太一样,660平台通过调用v4l-subdev的ioctl传递camera sensor的参数,在
新平台上,通过ion拿取camera sensor参数.
	
下面来分析过程:

怎么用的?

cam_sensor_subdev_ioctl过程(在vendor层通过调用media->ioctl获取与camera相关的节点后,打开v4l2-subdev部分)

我们从 cam_sensor_subdev_ioctl()函数开始:
	/*1*/cam_sensor_subdev_ioctl(){
		.....
		struct cam_sensor_ctrl_t *s_ctrl =
			v4l2_get_subdevdata(sd);	/* 通过之前的分析,sd是在之前流程中拿到video_device下的参数,其中是没有与sensor相关参数的*/
		switch (cmd) {
		case VIDIOC_CAM_CONTROL:
			rc = cam_sensor_driver_cmd(s_ctrl, arg);/* 主要看这个函数 */
			break;
		}
		return rc;
		.....
	}
		/*2*/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;
			}
			...../* 后面probe 之前已经分析过了 */
		}
				/*3*/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;
						}
					}

				}
						/*4*/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;
}
  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值