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

Commit 04d3cded authored by Kyle Yan's avatar Kyle Yan Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: cam: reqmgr: Add request manager driver" into msm-4.9

parents 9f7dc0be 8d91a62b
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