blob: 4e9034e2a76ab97c7428698d1622d6d1373201c2 [file] [log] [blame]
/* Copyright (c) 2017-2018, 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/debugfs.h>
#include <linux/videodev2.h>
#include <linux/uaccess.h>
#include "cam_node.h"
#include "cam_trace.h"
#include "cam_debug_util.h"
static struct cam_context *cam_node_get_ctxt_from_free_list(
struct cam_node *node)
{
struct cam_context *ctx = NULL;
mutex_lock(&node->list_mutex);
if (!list_empty(&node->free_ctx_list)) {
ctx = list_first_entry(&node->free_ctx_list,
struct cam_context, list);
list_del_init(&ctx->list);
}
mutex_unlock(&node->list_mutex);
if (ctx)
kref_init(&ctx->refcount);
return ctx;
}
void cam_node_put_ctxt_to_free_list(struct kref *ref)
{
struct cam_context *ctx =
container_of(ref, struct cam_context, refcount);
struct cam_node *node = ctx->node;
mutex_lock(&node->list_mutex);
list_add_tail(&ctx->list, &node->free_ctx_list);
mutex_unlock(&node->list_mutex);
}
static int __cam_node_handle_query_cap(struct cam_node *node,
struct cam_query_cap_cmd *query)
{
int rc = -EFAULT;
if (!query) {
CAM_ERR(CAM_CORE, "Invalid params");
return -EINVAL;
}
if (node->hw_mgr_intf.hw_get_caps) {
rc = node->hw_mgr_intf.hw_get_caps(
node->hw_mgr_intf.hw_mgr_priv, query);
}
return rc;
}
static int __cam_node_handle_acquire_dev(struct cam_node *node,
struct cam_acquire_dev_cmd *acquire)
{
int rc = 0;
struct cam_context *ctx = NULL;
if (!acquire)
return -EINVAL;
ctx = cam_node_get_ctxt_from_free_list(node);
if (!ctx) {
rc = -ENOMEM;
goto err;
}
rc = cam_context_handle_acquire_dev(ctx, acquire);
if (rc) {
CAM_ERR(CAM_CORE, "Acquire device failed for node %s",
node->name);
goto free_ctx;
}
return 0;
free_ctx:
cam_context_putref(ctx);
err:
return rc;
}
static int __cam_node_handle_start_dev(struct cam_node *node,
struct cam_start_stop_dev_cmd *start)
{
struct cam_context *ctx = NULL;
int rc;
if (!start)
return -EINVAL;
if (start->dev_handle <= 0) {
CAM_ERR(CAM_CORE, "Invalid device handle for context");
return -EINVAL;
}
if (start->session_handle <= 0) {
CAM_ERR(CAM_CORE, "Invalid session handle for context");
return -EINVAL;
}
ctx = (struct cam_context *)cam_get_device_priv(start->dev_handle);
if (!ctx) {
CAM_ERR(CAM_CORE, "Can not get context for handle %d",
start->dev_handle);
return -EINVAL;
}
rc = cam_context_handle_start_dev(ctx, start);
if (rc)
CAM_ERR(CAM_CORE, "Start failure for node %s", node->name);
return rc;
}
static int __cam_node_handle_stop_dev(struct cam_node *node,
struct cam_start_stop_dev_cmd *stop)
{
struct cam_context *ctx = NULL;
int rc;
if (!stop)
return -EINVAL;
if (stop->dev_handle <= 0) {
CAM_ERR(CAM_CORE, "Invalid device handle for context");
return -EINVAL;
}
if (stop->session_handle <= 0) {
CAM_ERR(CAM_CORE, "Invalid session handle for context");
return -EINVAL;
}
ctx = (struct cam_context *)cam_get_device_priv(stop->dev_handle);
if (!ctx) {
CAM_ERR(CAM_CORE, "Can not get context for handle %d",
stop->dev_handle);
return -EINVAL;
}
rc = cam_context_handle_stop_dev(ctx, stop);
if (rc)
CAM_ERR(CAM_CORE, "Stop failure for node %s", node->name);
return rc;
}
static int __cam_node_handle_config_dev(struct cam_node *node,
struct cam_config_dev_cmd *config)
{
struct cam_context *ctx = NULL;
int rc;
if (!config)
return -EINVAL;
if (config->dev_handle <= 0) {
CAM_ERR(CAM_CORE, "Invalid device handle for context");
return -EINVAL;
}
if (config->session_handle <= 0) {
CAM_ERR(CAM_CORE, "Invalid session handle for context");
return -EINVAL;
}
ctx = (struct cam_context *)cam_get_device_priv(config->dev_handle);
if (!ctx) {
CAM_ERR(CAM_CORE, "Can not get context for handle %d",
config->dev_handle);
return -EINVAL;
}
rc = cam_context_handle_config_dev(ctx, config);
if (rc)
CAM_ERR(CAM_CORE, "Config failure for node %s", node->name);
return rc;
}
static int __cam_node_handle_flush_dev(struct cam_node *node,
struct cam_flush_dev_cmd *flush)
{
struct cam_context *ctx = NULL;
int rc;
if (!flush)
return -EINVAL;
if (flush->dev_handle <= 0) {
CAM_ERR(CAM_CORE, "Invalid device handle for context");
return -EINVAL;
}
if (flush->session_handle <= 0) {
CAM_ERR(CAM_CORE, "Invalid session handle for context");
return -EINVAL;
}
ctx = (struct cam_context *)cam_get_device_priv(flush->dev_handle);
if (!ctx) {
CAM_ERR(CAM_CORE, "Can not get context for handle %d",
flush->dev_handle);
return -EINVAL;
}
rc = cam_context_handle_flush_dev(ctx, flush);
if (rc)
CAM_ERR(CAM_CORE, "Flush failure for node %s", node->name);
return rc;
}
static int __cam_node_handle_release_dev(struct cam_node *node,
struct cam_release_dev_cmd *release)
{
int rc = 0;
struct cam_context *ctx = NULL;
if (!release)
return -EINVAL;
if (release->dev_handle <= 0) {
CAM_ERR(CAM_CORE, "Invalid device handle for context");
return -EINVAL;
}
if (release->session_handle <= 0) {
CAM_ERR(CAM_CORE, "Invalid session handle for context");
return -EINVAL;
}
ctx = (struct cam_context *)cam_get_device_priv(release->dev_handle);
if (!ctx) {
CAM_ERR(CAM_CORE, "Can not get context for handle %d node %s",
release->dev_handle, node->name);
return -EINVAL;
}
rc = cam_context_handle_release_dev(ctx, release);
if (rc)
CAM_ERR(CAM_CORE, "context release failed node %s", node->name);
rc = cam_destroy_device_hdl(release->dev_handle);
if (rc)
CAM_ERR(CAM_CORE, "destroy device handle is failed node %s",
node->name);
cam_context_putref(ctx);
return rc;
}
static int __cam_node_crm_get_dev_info(struct cam_req_mgr_device_info *info)
{
struct cam_context *ctx = NULL;
if (!info)
return -EINVAL;
ctx = (struct cam_context *) cam_get_device_priv(info->dev_hdl);
if (!ctx) {
CAM_ERR(CAM_CORE, "Can not get context for handle %d",
info->dev_hdl);
return -EINVAL;
}
return cam_context_handle_crm_get_dev_info(ctx, info);
}
static int __cam_node_crm_link_setup(
struct cam_req_mgr_core_dev_link_setup *setup)
{
int rc;
struct cam_context *ctx = NULL;
if (!setup)
return -EINVAL;
ctx = (struct cam_context *) cam_get_device_priv(setup->dev_hdl);
if (!ctx) {
CAM_ERR(CAM_CORE, "Can not get context for handle %d",
setup->dev_hdl);
return -EINVAL;
}
if (setup->link_enable)
rc = cam_context_handle_crm_link(ctx, setup);
else
rc = cam_context_handle_crm_unlink(ctx, setup);
return rc;
}
static int __cam_node_crm_apply_req(struct cam_req_mgr_apply_request *apply)
{
struct cam_context *ctx = NULL;
if (!apply)
return -EINVAL;
ctx = (struct cam_context *) cam_get_device_priv(apply->dev_hdl);
if (!ctx) {
CAM_ERR(CAM_CORE, "Can not get context for handle %d",
apply->dev_hdl);
return -EINVAL;
}
trace_cam_apply_req("Node", apply->request_id);
return cam_context_handle_crm_apply_req(ctx, apply);
}
static int __cam_node_crm_flush_req(struct cam_req_mgr_flush_request *flush)
{
struct cam_context *ctx = NULL;
if (!flush) {
CAM_ERR(CAM_CORE, "Invalid flush request payload");
return -EINVAL;
}
ctx = (struct cam_context *) cam_get_device_priv(flush->dev_hdl);
if (!ctx) {
CAM_ERR(CAM_CORE, "Can not get context for handle %d",
flush->dev_hdl);
return -EINVAL;
}
return cam_context_handle_crm_flush_req(ctx, flush);
}
static int __cam_node_crm_process_evt(
struct cam_req_mgr_link_evt_data *evt_data)
{
struct cam_context *ctx = NULL;
if (!evt_data) {
CAM_ERR(CAM_CORE, "Invalid process event request payload");
return -EINVAL;
}
ctx = (struct cam_context *) cam_get_device_priv(evt_data->dev_hdl);
if (!ctx) {
CAM_ERR(CAM_CORE, "Can not get context for handle %d",
evt_data->dev_hdl);
return -EINVAL;
}
return cam_context_handle_crm_process_evt(ctx, evt_data);
}
int cam_node_deinit(struct cam_node *node)
{
if (node)
memset(node, 0, sizeof(*node));
CAM_DBG(CAM_CORE, "deinit complete");
return 0;
}
int cam_node_shutdown(struct cam_node *node)
{
int i = 0;
if (!node)
return -EINVAL;
for (i = 0; i < node->ctx_size; i++) {
if (node->ctx_list[i].dev_hdl >= 0) {
cam_context_shutdown(&(node->ctx_list[i]));
cam_destroy_device_hdl(node->ctx_list[i].dev_hdl);
cam_context_putref(&(node->ctx_list[i]));
}
}
if (node->hw_mgr_intf.hw_close)
node->hw_mgr_intf.hw_close(node->hw_mgr_intf.hw_mgr_priv,
NULL);
return 0;
}
int cam_node_init(struct cam_node *node, struct cam_hw_mgr_intf *hw_mgr_intf,
struct cam_context *ctx_list, uint32_t ctx_size, char *name)
{
int rc = 0;
int i;
if (!node || !hw_mgr_intf ||
sizeof(node->hw_mgr_intf) != sizeof(*hw_mgr_intf)) {
return -EINVAL;
}
memset(node, 0, sizeof(*node));
strlcpy(node->name, name, sizeof(node->name));
memcpy(&node->hw_mgr_intf, hw_mgr_intf, sizeof(node->hw_mgr_intf));
node->crm_node_intf.apply_req = __cam_node_crm_apply_req;
node->crm_node_intf.get_dev_info = __cam_node_crm_get_dev_info;
node->crm_node_intf.link_setup = __cam_node_crm_link_setup;
node->crm_node_intf.flush_req = __cam_node_crm_flush_req;
node->crm_node_intf.process_evt = __cam_node_crm_process_evt;
mutex_init(&node->list_mutex);
INIT_LIST_HEAD(&node->free_ctx_list);
node->ctx_list = ctx_list;
node->ctx_size = ctx_size;
for (i = 0; i < ctx_size; i++) {
if (!ctx_list[i].state_machine) {
CAM_ERR(CAM_CORE,
"camera context %d is not initialized", i);
rc = -1;
goto err;
}
INIT_LIST_HEAD(&ctx_list[i].list);
list_add_tail(&ctx_list[i].list, &node->free_ctx_list);
ctx_list[i].node = node;
}
node->state = CAM_NODE_STATE_INIT;
err:
CAM_DBG(CAM_CORE, "Exit. (rc = %d)", rc);
return rc;
}
int cam_node_handle_ioctl(struct cam_node *node, struct cam_control *cmd)
{
int rc = 0;
if (!cmd)
return -EINVAL;
CAM_DBG(CAM_CORE, "handle cmd %d", cmd->op_code);
switch (cmd->op_code) {
case CAM_QUERY_CAP: {
struct cam_query_cap_cmd query;
if (copy_from_user(&query, (void __user *)cmd->handle,
sizeof(query))) {
rc = -EFAULT;
break;
}
rc = __cam_node_handle_query_cap(node, &query);
if (rc) {
CAM_ERR(CAM_CORE, "querycap is failed(rc = %d)",
rc);
break;
}
if (copy_to_user((void __user *)cmd->handle, &query,
sizeof(query)))
rc = -EFAULT;
break;
}
case CAM_ACQUIRE_DEV: {
struct cam_acquire_dev_cmd acquire;
if (copy_from_user(&acquire, (void __user *)cmd->handle,
sizeof(acquire))) {
rc = -EFAULT;
break;
}
rc = __cam_node_handle_acquire_dev(node, &acquire);
if (rc) {
CAM_ERR(CAM_CORE, "acquire device failed(rc = %d)",
rc);
break;
}
if (copy_to_user((void __user *)cmd->handle, &acquire,
sizeof(acquire)))
rc = -EFAULT;
break;
}
case CAM_START_DEV: {
struct cam_start_stop_dev_cmd start;
if (copy_from_user(&start, (void __user *)cmd->handle,
sizeof(start)))
rc = -EFAULT;
else {
rc = __cam_node_handle_start_dev(node, &start);
if (rc)
CAM_ERR(CAM_CORE,
"start device failed(rc = %d)", rc);
}
break;
}
case CAM_STOP_DEV: {
struct cam_start_stop_dev_cmd stop;
if (copy_from_user(&stop, (void __user *)cmd->handle,
sizeof(stop)))
rc = -EFAULT;
else {
rc = __cam_node_handle_stop_dev(node, &stop);
if (rc)
CAM_ERR(CAM_CORE,
"stop device failed(rc = %d)", rc);
}
break;
}
case CAM_CONFIG_DEV: {
struct cam_config_dev_cmd config;
if (copy_from_user(&config, (void __user *)cmd->handle,
sizeof(config)))
rc = -EFAULT;
else {
rc = __cam_node_handle_config_dev(node, &config);
if (rc)
CAM_ERR(CAM_CORE,
"config device failed(rc = %d)", rc);
}
break;
}
case CAM_RELEASE_DEV: {
struct cam_release_dev_cmd release;
if (copy_from_user(&release, (void __user *)cmd->handle,
sizeof(release)))
rc = -EFAULT;
else {
rc = __cam_node_handle_release_dev(node, &release);
if (rc)
CAM_ERR(CAM_CORE,
"release device failed(rc = %d)", rc);
}
break;
}
case CAM_FLUSH_REQ: {
struct cam_flush_dev_cmd flush;
if (copy_from_user(&flush, (void __user *)cmd->handle,
sizeof(flush)))
rc = -EFAULT;
else {
rc = __cam_node_handle_flush_dev(node, &flush);
if (rc)
CAM_ERR(CAM_CORE,
"flush device failed(rc = %d)", rc);
}
break;
}
default:
CAM_ERR(CAM_CORE, "Unknown op code %d", cmd->op_code);
rc = -EINVAL;
}
return rc;
}