Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8d91a62b authored by Sagar Gore's avatar Sagar Gore Committed by Gerrit - the friendly Code Review server
Browse files

msm: cam: reqmgr: Add request manager driver



Add camera request manager which communicates with different
camera hardware drivers for synchronization of applying
particular settings. These settings will be applied for
every frame. Below are the changes included:
- Create and destroy of session and link
- Utilities functions for camera request manager
- Interface to userspace component
- Setup workqueue for handling tasks such as schedule and process request

CRs-Fixed: 1108532
Change-Id: Ibb436b7aa161ebbedff808da2ac86221720f9af1
Signed-off-by: default avatarSagar Gore <sgore@codeaurora.org>
Signed-off-by: default avatarSuresh Vankadara <svankada@codeaurora.org>
parent 25f739ab
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
obj-$(CONFIG_SPECTRA_CAMERA) += cam_req_mgr_dev.o
obj-$(CONFIG_SPECTRA_CAMERA) += cam_req_mgr_dev.o cam_req_mgr_util.o cam_req_mgr_core.o  cam_req_mgr_workq.o
+659 −0
Original line number Diff line number Diff line
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include "cam_req_mgr_interface.h"
#include "cam_req_mgr_util.h"
#include "cam_req_mgr_core.h"
#include "cam_req_mgr_workq.h"

/* Forward declarations */
static int cam_req_mgr_cb_notify_sof(
	struct cam_req_mgr_sof_notify *sof_data);


static struct cam_req_mgr_core_device *g_crm_core_dev;

static struct cam_req_mgr_crm_cb cam_req_mgr_ops = {
	.notify_sof = cam_req_mgr_cb_notify_sof,
	.notify_err = NULL,
	.add_req = NULL,
};

/**
 * cam_req_mgr_pvt_find_link()
 *
 * @brief: Finds link matching with handle within session
 * @session: session indetifier
 * @link_hdl: link handle
 *
 * Returns pointer to link matching handle
 */
static struct cam_req_mgr_core_link *cam_req_mgr_pvt_find_link(
	struct cam_req_mgr_core_session *session, int32_t link_hdl)
{
	int32_t i;
	struct cam_req_mgr_core_link *link = NULL;

	if (!session) {
		CRM_ERR("NULL session ptr");
		return NULL;
	}

	spin_lock(&session->lock);
	for (i = 0; i < MAX_LINKS_PER_SESSION; i++) {
		link = &session->links[i];
		spin_lock(&link->lock);
		if (link->link_hdl == link_hdl) {
			CRM_DBG("Link found p_delay %d",
				 link->max_pipeline_delay);
			spin_unlock(&link->lock);
			break;
		}
		spin_unlock(&link->lock);
	}
	if (i >= MAX_LINKS_PER_SESSION)
		link = NULL;
	spin_unlock(&session->lock);

	return link;
}

/**
 * cam_req_mgr_process_sof()
 *
 * @brief: This runs in workque thread context. Call core funcs to check
 * which peding requests can be processed.
 * @data:contains information about frame_id, link etc.
 *
 * Returns 0 on success.
 */
static int cam_req_mgr_process_sof(void *priv, void *data)
{
	int ret = 0, i = 0;
	struct cam_req_mgr_sof_notify *sof_data = NULL;
	struct cam_req_mgr_core_link *link = NULL;
	struct cam_req_mgr_connected_device *device = NULL;
	struct cam_req_mgr_apply_request apply_req;

	if (!data || !priv) {
		CRM_ERR("input args NULL %pK %pK", data, priv);
		ret = -EINVAL;
		goto end;
	}
	link = (struct cam_req_mgr_core_link *)priv;
	sof_data = (struct cam_req_mgr_sof_notify *)data;

	CRM_DBG("link_hdl %x frame_id %lld",
		sof_data->link_hdl,
		sof_data->frame_id);

	apply_req.link_hdl = sof_data->link_hdl;
	/* @TODO: go through request table and issue
	 * request id based on dev status
	 */
	apply_req.request_id = sof_data->frame_id;
	apply_req.report_if_bubble = 0;

	CRM_DBG("link %pK l_dev %pK num_dev %d",
		link, link->l_devices, link->num_connections);
	for (i = 0; i < link->num_connections; i++) {
		device = &link->l_devices[i];
		if (device != NULL) {
			CRM_DBG("dev_id %d dev_hdl %x ops %pK p_delay %d",
				device->dev_info.dev_id, device->dev_hdl,
				device->ops, device->dev_info.p_delay);
			apply_req.dev_hdl = device->dev_hdl;
			if (device->ops && device->ops->apply_req) {
				ret = device->ops->apply_req(&apply_req);
				/* Error handling for this failure is pending */
				if (ret < 0)
					CRM_ERR("Failure:%d dev=%d", ret,
						device->dev_info.dev_id);
			}

		}
	}

end:
	return ret;
}

/**
 * cam_req_mgr_notify_sof()
 *
 * @brief: SOF received from device, sends trigger through workqueue
 * @sof_data: contains information about frame_id, link etc.
 *
 * Returns 0 on success
 */
static int cam_req_mgr_cb_notify_sof(struct cam_req_mgr_sof_notify *sof_data)
{
	int                           ret = 0;
	struct crm_workq_task        *task = NULL;
	struct cam_req_mgr_core_link *link = NULL;

	if (!sof_data) {
		CRM_ERR("sof_data is NULL");
		ret = -EINVAL;
		goto end;
	}

	CRM_DBG("link_hdl %x frame_id %lld",
		sof_data->link_hdl,
		sof_data->frame_id);

	link = (struct cam_req_mgr_core_link *)
		cam_get_device_priv(sof_data->link_hdl);
	if (!link) {
		CRM_DBG("link ptr NULL %x", sof_data->link_hdl);
		ret = -EINVAL;
		goto end;

	}

	task = cam_req_mgr_workq_get_task(link->workq);
	if (!task) {
		CRM_ERR("no empty task frame %lld", sof_data->frame_id);
		ret = -EBUSY;
		goto end;
	}
	task->type = CRM_WORKQ_TASK_NOTIFY_SOF;
	task->u.notify_sof.frame_id = sof_data->frame_id;
	task->u.notify_sof.link_hdl = sof_data->link_hdl;
	task->u.notify_sof.dev_hdl = sof_data->dev_hdl;
	task->process_cb = &cam_req_mgr_process_sof;
	task->priv = link;
	cam_req_mgr_workq_enqueue_task(task);

end:
	return ret;
}

/**
 * cam_req_mgr_pvt_reserve_link()
 *
 * @brief: Reserves one link data struct within session
 * @session: session identifier
 *
 * Returns pointer to link reserved
 */
static struct cam_req_mgr_core_link *cam_req_mgr_pvt_reserve_link(
	struct cam_req_mgr_core_session *session)
{
	int32_t i;
	struct cam_req_mgr_core_link *link;

	if (!session) {
		CRM_ERR("NULL session ptr");
		return NULL;
	}

	spin_lock(&session->lock);
	for (i = 0; i < MAX_LINKS_PER_SESSION; i++) {
		link = &session->links[i];
		spin_lock(&link->lock);
		if (link->link_state == CAM_CRM_LINK_STATE_AVAILABLE) {
			link->num_connections = 0;
			link->max_pipeline_delay = 0;
			memset(link->req_table, 0,
				sizeof(struct cam_req_mgr_request_table));
			link->link_state = CAM_CRM_LINK_STATE_IDLE;
			spin_unlock(&link->lock);
			break;
		}
		spin_unlock(&link->lock);
	}
	CRM_DBG("Link available (total %d)", session->num_active_links);
	spin_unlock(&session->lock);

	if (i >= MAX_LINKS_PER_SESSION)
		link = NULL;

	return link;
}

/**
 * cam_req_mgr_pvt_create_subdevs()
 *
 * @brief: Create new crm  subdev to link with realtime devices
 * @l_devices: list of subdevs internal to crm
 * @num_dev: num of subdevs to be created for link
 *
 * Returns pointer to allocated list of devices
 */
static struct cam_req_mgr_connected_device *
	cam_req_mgr_pvt_create_subdevs(int32_t num_dev)
{
	struct cam_req_mgr_connected_device *l_devices;

	l_devices = (struct cam_req_mgr_connected_device *)
		kzalloc(sizeof(struct cam_req_mgr_connected_device) * num_dev,
		GFP_KERNEL);
	if (!l_devices)
		CRM_DBG("Insufficient memory %lu",
			sizeof(struct cam_req_mgr_connected_device) * num_dev);

	return l_devices;
}

/**
 * cam_req_mgr_pvt_destroy_subdev()
 *
 * @brief: Cleans up the subdevs allocated by crm for link
 * @l_device: pointer to list of subdevs crm created
 *
 * Returns 0 for success
 */
static int cam_req_mgr_pvt_destroy_subdev(
	struct cam_req_mgr_connected_device **l_device)
{
	int ret = 0;

	if (!(*l_device))
		ret = -EINVAL;
	else {
		kfree(*l_device);
		*l_device = NULL;
	}

	return ret;
}

int cam_req_mgr_create_session(
	struct cam_req_mgr_session_info *ses_info)
{
	int ret = 0;
	int32_t i;
	int32_t session_hdl;
	struct cam_req_mgr_core_session *cam_session;

	if (!ses_info) {
		CRM_ERR("NULL session info pointer");
		return -EINVAL;
	}
	mutex_lock(&g_crm_core_dev->crm_lock);
	cam_session = (struct cam_req_mgr_core_session *)
		kzalloc(sizeof(*cam_session), GFP_KERNEL);
	if (!cam_session) {
		ret = -ENOMEM;
		goto end;
	}

	session_hdl = cam_create_session_hdl((void *)cam_session);
	if (session_hdl < 0) {
		CRM_ERR("unable to create session_hdl = %x", session_hdl);
		ret = session_hdl;
		goto session_hdl_failed;
	}
	ses_info->session_hdl = session_hdl;
	cam_session->session_hdl = session_hdl;

	spin_lock_init(&cam_session->lock);
	cam_session->num_active_links = 0;

	for (i = 0; i < MAX_LINKS_PER_SESSION; i++) {
		spin_lock_init(&cam_session->links[i].lock);
		cam_session->links[i].link_state = CAM_CRM_LINK_STATE_AVAILABLE;
		INIT_LIST_HEAD(&cam_session->links[i].link_head);
		cam_session->links[i].workq = NULL;
	}
	list_add(&cam_session->entry, &g_crm_core_dev->session_head);

	mutex_unlock(&g_crm_core_dev->crm_lock);
	return ret;

session_hdl_failed:
	kfree(cam_session);
end:
	mutex_unlock(&g_crm_core_dev->crm_lock);
	return ret;
}

int cam_req_mgr_destroy_session(
		struct cam_req_mgr_session_info *ses_info)
{
	int ret;
	int32_t i;
	struct cam_req_mgr_core_session *cam_session;
	struct cam_req_mgr_core_link *link = NULL;

	if (!ses_info) {
		CRM_ERR("NULL session info pointer");
		return -EINVAL;
	}

	mutex_lock(&g_crm_core_dev->crm_lock);
	cam_session = (struct cam_req_mgr_core_session *)
		cam_get_device_priv(ses_info->session_hdl);
	if (cam_session == NULL) {
		CRM_ERR("failed to get session priv");
		ret = -ENOENT;
		goto end;

	}
	spin_lock(&cam_session->lock);
	for (i = 0; i < cam_session->num_active_links; i++) {
		link = &cam_session->links[i];
		CRM_ERR("session %x active_links %d hdl %x connections %d",
			ses_info->session_hdl,
			cam_session->num_active_links,
			link->link_hdl, link->num_connections);
	}
	list_del(&cam_session->entry);
	spin_unlock(&cam_session->lock);
	kfree(cam_session);

	ret = cam_destroy_session_hdl(ses_info->session_hdl);
	if (ret)
		CRM_ERR("unable to destroy session_hdl = %x ret %d",
			ses_info->session_hdl, ret);

end:
	mutex_unlock(&g_crm_core_dev->crm_lock);
	return ret;

}

int cam_req_mgr_link(struct cam_req_mgr_link_info *link_info)
{
	int ret = 0;
	int32_t i, link_hdl;
	char buf[128];
	struct cam_create_dev_hdl root_dev;
	struct cam_req_mgr_core_session *cam_session;
	struct cam_req_mgr_core_link *link;
	struct cam_req_mgr_core_dev_link_setup link_data;
	struct cam_req_mgr_connected_device *l_devices;
	enum cam_pipeline_delay max_delay = CAM_PIPELINE_DELAY_0;

	if (!link_info) {
		CRM_ERR("NULL pointer");
		return -EINVAL;
	}

	if (link_info->num_devices > CAM_REQ_MGR_MAX_HANDLES) {
		CRM_ERR("Invalid num devices %d", link_info->num_devices);
		return -EINVAL;
	}

	cam_session = (struct cam_req_mgr_core_session *)
		cam_get_device_priv(link_info->session_hdl);
	if (!cam_session) {
		CRM_ERR("NULL session pointer");
		return -EINVAL;
	}

	link = cam_req_mgr_pvt_reserve_link(cam_session);
	if (!link) {
		CRM_ERR("NULL link pointer");
		return -EINVAL;
	}

	memset(&root_dev, 0, sizeof(struct cam_create_dev_hdl));
	root_dev.session_hdl = link_info->session_hdl;
	root_dev.priv = (void *)link;

	link_hdl = cam_create_device_hdl(&root_dev);
	if (link_hdl < 0) {
		CRM_ERR("Insufficient memory to create new device handle");
		ret = link_hdl;
		goto link_hdl_fail;
	}

	l_devices = cam_req_mgr_pvt_create_subdevs(link_info->num_devices);
	if (!l_devices) {
		ret = -ENOMEM;
		goto create_subdev_failed;
	}

	for (i = 0; i < link_info->num_devices; i++) {
		l_devices[i].dev_hdl = link_info->dev_hdls[i];
		l_devices[i].parent = (void *)link;
		l_devices[i].ops = (struct cam_req_mgr_kmd_ops *)
			cam_get_device_ops(link_info->dev_hdls[i]);
		link_data.dev_hdl = l_devices[i].dev_hdl;
		l_devices[i].dev_info.dev_hdl = l_devices[i].dev_hdl;
		if (l_devices[i].ops) {
			if (l_devices[i].ops->get_dev_info) {
				ret = l_devices[i].ops->get_dev_info(
					&l_devices[i].dev_info);
				if (ret < 0 ||
					l_devices[i].dev_info.p_delay >=
					CAM_PIPELINE_DELAY_MAX ||
					l_devices[i].dev_info.p_delay <
					CAM_PIPELINE_DELAY_0) {
					CRM_ERR("get device info failed");
					goto error;
				} else {
					CRM_DBG("%x: connected: %s, delay %d",
						link_info->session_hdl,
						l_devices[i].dev_info.name,
						l_devices[i].dev_info.p_delay);
					if (l_devices[i].dev_info.p_delay >
						max_delay)
					max_delay =
						l_devices[i].dev_info.p_delay;
				}
			}
		} else {
			CRM_ERR("FATAL: device ops NULL");
			ret = -ENXIO;
			goto error;
		}
	}

	link_data.link_enable = true;
	link_data.link_hdl = link_hdl;
	link_data.crm_cb = &cam_req_mgr_ops;
	link_data.max_delay = max_delay;

	/* After getting info about all devices, establish link */
	for (i = 0; i < link_info->num_devices; i++) {
		l_devices[i].dev_hdl = link_info->dev_hdls[i];
		l_devices[i].parent = (void *)link;
		l_devices[i].ops = (struct cam_req_mgr_kmd_ops *)
			cam_get_device_ops(link_info->dev_hdls[i]);
		link_data.dev_hdl = l_devices[i].dev_hdl;
		l_devices[i].dev_info.dev_hdl = l_devices[i].dev_hdl;
		if (l_devices[i].ops) {
			if (l_devices[i].ops->link_setup) {
				ret = l_devices[i].ops->link_setup(&link_data);
				if (ret < 0) {
					/* TODO check handlng of this failure */
					CRM_ERR("link setup failed");
					goto error;
				}
			}
		}
		list_add_tail(&l_devices[i].entry, &link->link_head);
	}

	/* Create worker for current link */
	snprintf(buf, sizeof(buf), "%x-%x", link_info->session_hdl, link_hdl);
	ret = cam_req_mgr_workq_create(buf, &link->workq);
	if (ret < 0) {
		CRM_ERR("FATAL: unable to create worker");
		goto error;
	}

	link_info->link_hdl = link_hdl;
	spin_lock(&link->lock);
	link->l_devices = l_devices;
	link->link_hdl = link_hdl;
	link->parent = (void *)cam_session;
	link->num_connections = link_info->num_devices;
	link->link_state = CAM_CRM_LINK_STATE_READY;
	spin_unlock(&link->lock);

	spin_lock(&cam_session->lock);
	cam_session->num_active_links++;
	spin_unlock(&cam_session->lock);

	return ret;

error:
	cam_req_mgr_pvt_destroy_subdev(&l_devices);
create_subdev_failed:
	cam_destroy_device_hdl(link_hdl);
link_hdl_fail:
	spin_lock(&link->lock);
	link->link_state = CAM_CRM_LINK_STATE_AVAILABLE;
	spin_unlock(&link->lock);

	return ret;
}

int cam_req_mgr_unlink(struct cam_req_mgr_unlink_info *unlink_info)
{
	int ret = 0;
	int32_t i = 0;
	struct cam_req_mgr_core_session *cam_session;
	struct cam_req_mgr_core_link *link;
	struct cam_req_mgr_connected_device *device;
	struct cam_req_mgr_core_dev_link_setup link_data;

	if (!unlink_info) {
		CRM_ERR("NULL pointer");
		return -EINVAL;
	}
	cam_session = (struct cam_req_mgr_core_session *)
	cam_get_device_priv(unlink_info->session_hdl);
	if (!cam_session) {
		CRM_ERR("NULL pointer");
		return -EINVAL;
	}

	link = cam_req_mgr_pvt_find_link(cam_session,
		unlink_info->link_hdl);
	if (!link) {
		CRM_ERR("NULL pointer");
		return -EINVAL;
	}

	ret = cam_destroy_device_hdl(link->link_hdl);
	if (ret < 0) {
		CRM_ERR("error in destroying dev handle %d %x",
			ret, link->link_hdl);
		ret = -EINVAL;
	}
	link_data.link_enable = false;
	link_data.link_hdl = link->link_hdl;
	link_data.crm_cb = NULL;
	for (i = 0; i < link->num_connections; i++) {
		device = &link->l_devices[i];
		link_data.dev_hdl = device->dev_hdl;
		if (device->ops && device->ops->link_setup)
			device->ops->link_setup(&link_data);
		device->dev_hdl = 0;
		device->parent = NULL;
		device->ops = NULL;
		list_del(&device->entry);
	}
	/* Destroy worker of link */
	cam_req_mgr_workq_destroy(link->workq);
	spin_lock(&link->lock);
	link->link_state = CAM_CRM_LINK_STATE_AVAILABLE;
	link->parent = NULL;
	link->num_connections = 0;
	link->link_hdl = 0;
	link->workq = NULL;
	spin_unlock(&link->lock);

	spin_lock(&cam_session->lock);
	cam_session->num_active_links--;
	spin_unlock(&cam_session->lock);

	ret = cam_req_mgr_pvt_destroy_subdev(&link->l_devices);
	if (ret < 0) {
		CRM_ERR("error while destroying subdev link %x",
			link_data.link_hdl);
		ret = -EINVAL;
	}

	return ret;
}

int cam_req_mgr_schedule_request(
			struct cam_req_mgr_sched_request *sched_req)
{
	if (!sched_req) {
		CRM_ERR("NULL pointer");
		return -EINVAL;
	}

	/* This function handles ioctl, implementation pending */
	return 0;
}

int cam_req_mgr_sync_mode(
			struct cam_req_mgr_sync_mode *sync_links)
{
	if (!sync_links) {
		CRM_ERR("NULL pointer");
		return -EINVAL;
	}

	/* This function handles ioctl, implementation pending */
	return 0;
}

int cam_req_mgr_flush_requests(
			struct cam_req_mgr_flush_info *flush_info)
{
	if (!flush_info) {
		CRM_ERR("NULL pointer");
		return -EINVAL;
	}

	/* This function handles ioctl, implementation pending */
	return 0;
}


int cam_req_mgr_core_device_init(void)
{
	CRM_DBG("Enter g_crm_core_dev %pK", g_crm_core_dev);

	if (g_crm_core_dev) {
		CRM_WARN("core device is already initialized");
		return 0;
	}
	g_crm_core_dev = (struct cam_req_mgr_core_device *)
		kzalloc(sizeof(*g_crm_core_dev), GFP_KERNEL);
	if (!g_crm_core_dev)
		return -ENOMEM;

	CRM_DBG("g_crm_core_dev %pK", g_crm_core_dev);
	INIT_LIST_HEAD(&g_crm_core_dev->session_head);
	mutex_init(&g_crm_core_dev->crm_lock);

	return 0;
}

int cam_req_mgr_core_device_deinit(void)
{
	if (!g_crm_core_dev) {
		CRM_ERR("NULL pointer");
		return -EINVAL;
	}

	CRM_DBG("g_crm_core_dev %pK", g_crm_core_dev);
	mutex_destroy(&g_crm_core_dev->crm_lock);
	kfree(g_crm_core_dev);
	g_crm_core_dev = NULL;

	return 0;
}
+273 −0

File added.

Preview size limit exceeded, changes collapsed.

+44 −0
Original line number Diff line number Diff line
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
#ifndef _CAM_REQ_MGR_CORE_DEFS_H_
#define _CAM_REQ_MGR_CORE_DEFS_H_

#define CRM_TRACE_ENABLE 0
#define CRM_DEBUG_MUTEX 0

#if (CRM_TRACE_ENABLE == 1)
	#define CRM_DBG(fmt, args...) do { \
	trace_printk("%d: [crm_dbg] "fmt"\n", __LINE__, ##args); \
	pr_debug("%s:%d "fmt"\n", __func__, __LINE__, ##args); \
	} while (0)

	#define CRM_WARN(fmt, args...) do { \
	trace_printk("%d: [crm_warn] "fmt"\n", __LINE__, ##args); \
	pr_warn("%s:%d "fmt"\n", __func__, __LINE__, ##args); \
	} while (0)

	#define CRM_ERR(fmt, args...) do { \
	trace_printk("%d: [crm_err] "fmt"\n", __LINE__, ##args); \
	pr_err("%s:%d "fmt"\n", __func__, __LINE__, ##args);\
	} while (0)
#else
	#define CRM_DBG(fmt, args...) pr_debug("%s:%d "fmt"\n", \
	__func__, __LINE__, ##args)

	#define CRM_WARN(fmt, args...) pr_warn("%s:%d "fmt"\n", \
	__func__, __LINE__, ##args)

	#define CRM_ERR(fmt, args...) pr_err("%s:%d "fmt"\n", \
	__func__, __LINE__, ##args)
#endif
#endif
+234 −0
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@
#include <media/v4l2-ioctl.h>
#include <media/cam_req_mgr.h>
#include "cam_req_mgr_dev.h"
#include "cam_req_mgr_util.h"
#include "cam_req_mgr_core.h"
#include "cam_dev_mgr_util.h"

#define CAM_REQ_MGR_EVENT_MAX 30
@@ -92,8 +94,76 @@ static void cam_v4l2_device_cleanup(void)
	g_dev.v4l2_dev = NULL;
}

static int cam_req_mgr_open(struct file *filep)
{
	int rc;

	mutex_lock(&g_dev.cam_lock);
	if (g_dev.open_cnt >= 1) {
		rc = -EALREADY;
		goto end;
	}

	rc = v4l2_fh_open(filep);
	if (rc) {
		pr_err("v4l2_fh_open failed: %d\n", rc);
		goto end;
	}

	spin_lock_bh(&g_dev.cam_eventq_lock);
	g_dev.cam_eventq = filep->private_data;
	spin_unlock_bh(&g_dev.cam_eventq_lock);

	g_dev.open_cnt++;

end:
	mutex_unlock(&g_dev.cam_lock);
	return rc;
}

static unsigned int cam_req_mgr_poll(struct file *f,
	struct poll_table_struct *pll_table)
{
	int rc = 0;
	struct v4l2_fh *eventq = f->private_data;

	if (!eventq)
		return -EINVAL;

	poll_wait(f, &eventq->wait, pll_table);
	if (v4l2_event_pending(eventq))
		rc = POLLPRI;

	return rc;
}

static int cam_req_mgr_close(struct file *filep)
{
	mutex_lock(&g_dev.cam_lock);

	if (g_dev.open_cnt <= 0) {
		mutex_unlock(&g_dev.cam_lock);
		return -EINVAL;
	}

	g_dev.open_cnt--;
	v4l2_fh_release(filep);

	spin_lock_bh(&g_dev.cam_eventq_lock);
	g_dev.cam_eventq = NULL;
	spin_unlock_bh(&g_dev.cam_eventq_lock);

	cam_req_mgr_util_free_hdls();
	mutex_unlock(&g_dev.cam_lock);

	return 0;
}

static struct v4l2_file_operations g_cam_fops = {
	.owner  = THIS_MODULE,
	.open   = cam_req_mgr_open,
	.poll   = cam_req_mgr_poll,
	.release = cam_req_mgr_close,
	.unlocked_ioctl   = video_ioctl2,
#ifdef CONFIG_COMPAT
	.compat_ioctl32 = video_ioctl2,
@@ -112,9 +182,151 @@ static int cam_unsubscribe_event(struct v4l2_fh *fh,
	return v4l2_event_unsubscribe(fh, sub);
}

static long cam_private_ioctl(struct file *file, void *fh,
	bool valid_prio, unsigned int cmd, void *arg)
{
	int rc;
	struct cam_control *k_ioctl;

	if ((!arg) || (cmd != VIDIOC_CAM_CONTROL))
		return -EINVAL;

	k_ioctl = (struct cam_control *)arg;

	if (!k_ioctl->handle)
		return -EINVAL;

	switch (k_ioctl->op_code) {
	case CAM_REQ_MGR_CREATE_SESSION: {
		struct cam_req_mgr_session_info ses_info;

		if (k_ioctl->size != sizeof(ses_info))
			return -EINVAL;

		if (copy_from_user(&ses_info,
			(void *)k_ioctl->handle,
			k_ioctl->size)) {
			return -EFAULT;
		}

		rc = cam_req_mgr_create_session(&ses_info);
		if (!rc)
			if (copy_to_user((void *)k_ioctl->handle,
				&ses_info, k_ioctl->size))
				rc = -EFAULT;
		}
		break;

	case CAM_REQ_MGR_DESTROY_SESSION: {
		struct cam_req_mgr_session_info ses_info;

		if (k_ioctl->size != sizeof(ses_info))
			return -EINVAL;

		if (copy_from_user(&ses_info,
			(void *)k_ioctl->handle,
			k_ioctl->size)) {
			return -EFAULT;
		}

		rc = cam_req_mgr_destroy_session(&ses_info);
		}
		break;

	case CAM_REQ_MGR_LINK: {
		struct cam_req_mgr_link_info link_info;

		if (k_ioctl->size != sizeof(link_info))
			return -EINVAL;

		if (copy_from_user(&link_info,
			(void *)k_ioctl->handle,
			k_ioctl->size)) {
			return -EFAULT;
		}

		rc = cam_req_mgr_link(&link_info);
		if (!rc)
			if (copy_to_user((void *)k_ioctl->handle,
				&link_info, k_ioctl->size))
				rc = -EFAULT;
		}
		break;

	case CAM_REQ_MGR_UNLINK: {
		struct cam_req_mgr_unlink_info unlink_info;

		if (k_ioctl->size != sizeof(unlink_info))
			return -EINVAL;

		if (copy_from_user(&unlink_info,
			(void *)k_ioctl->handle,
			k_ioctl->size)) {
			return -EFAULT;
		}

		rc = cam_req_mgr_unlink(&unlink_info);
		}
		break;

	case CAM_REQ_MGR_SCHED_REQ: {
		struct cam_req_mgr_sched_request sched_req;

		if (k_ioctl->size != sizeof(sched_req))
			return -EINVAL;

		if (copy_from_user(&sched_req,
			(void *)k_ioctl->handle,
			k_ioctl->size)) {
			return -EFAULT;
		}

		rc = cam_req_mgr_schedule_request(&sched_req);
		}
		break;

	case CAM_REQ_MGR_FLUSH_REQ: {
		struct cam_req_mgr_flush_info flush_info;

		if (k_ioctl->size != sizeof(flush_info))
			return -EINVAL;

		if (copy_from_user(&flush_info,
			(void *)k_ioctl->handle,
			k_ioctl->size)) {
			return -EFAULT;
		}

		rc = cam_req_mgr_flush_requests(&flush_info);
		}
		break;

	case CAM_REQ_MGR_SYNC_MODE: {
		struct cam_req_mgr_sync_mode sync_mode;

		if (k_ioctl->size != sizeof(sync_mode))
			return -EINVAL;

		if (copy_from_user(&sync_mode,
			(void *)k_ioctl->handle,
			k_ioctl->size)) {
			return -EFAULT;
		}

		rc = cam_req_mgr_sync_mode(&sync_mode);
		}
		break;
	default:
		return -ENOIOCTLCMD;
	}

	return rc;
}

static const struct v4l2_ioctl_ops g_cam_ioctl_ops = {
	.vidioc_subscribe_event = cam_subscribe_event,
	.vidioc_unsubscribe_event = cam_unsubscribe_event,
	.vidioc_default = cam_private_ioctl,
};

static int cam_video_device_setup(void)
@@ -231,6 +443,8 @@ EXPORT_SYMBOL(cam_unregister_subdev);

static int cam_req_mgr_remove(struct platform_device *pdev)
{
	cam_req_mgr_core_device_deinit();
	cam_req_mgr_util_deinit();
	cam_media_device_cleanup();
	cam_video_device_cleanup();
	cam_v4l2_device_cleanup();
@@ -256,12 +470,32 @@ static int cam_req_mgr_probe(struct platform_device *pdev)
	if (rc)
		goto video_setup_fail;

	g_dev.open_cnt = 0;
	mutex_init(&g_dev.cam_lock);
	spin_lock_init(&g_dev.cam_eventq_lock);
	g_dev.subdev_nodes_created = false;
	mutex_init(&g_dev.dev_lock);

	rc = cam_req_mgr_util_init();
	if (rc) {
		pr_err("cam req mgr util init is failed\n");
		goto req_mgr_util_fail;
	}

	rc = cam_req_mgr_core_device_init();
	if (rc) {
		pr_err("core device setup failed\n");
		goto req_mgr_core_fail;
	}

	g_dev.state = true;

	return rc;

req_mgr_core_fail:
	cam_req_mgr_util_deinit();
req_mgr_util_fail:
	cam_video_device_cleanup();
video_setup_fail:
	cam_media_device_cleanup();
media_setup_fail:
Loading