linux/drivers/media/platform/st/sti/delta/delta-ipc.c

// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) STMicroelectronics SA 2015
 * Author: Hugues Fruchet <[email protected]> for STMicroelectronics.
 */

#include <linux/rpmsg.h>

#include "delta.h"
#include "delta-ipc.h"
#include "delta-mem.h"

#define IPC_TIMEOUT
#define IPC_SANITY_TAG

enum delta_ipc_fw_command {};

#define to_rpmsg_driver(__drv)
#define to_delta(__d)

#define to_ctx(hdl)
#define to_pctx(ctx)

struct delta_ipc_header_msg {};

#define to_host_hdl(ctx)

#define msg_to_ctx(msg)
#define msg_to_copro_hdl(msg)

static inline dma_addr_t to_paddr(struct delta_ipc_ctx *ctx, void *vaddr)
{}

static inline bool is_valid_data(struct delta_ipc_ctx *ctx,
				 void *data, u32 size)
{}

/*
 * IPC shared memory (@ipc_buf_size, @ipc_buf_paddr) is sent to copro
 * at each instance opening. This memory is allocated by IPC client
 * and given through delta_ipc_open(). All messages parameters
 * (open, set_stream, decode) will have their phy address within
 * this IPC shared memory, avoiding de-facto recopies inside delta-ipc.
 * All the below messages structures are used on both host and firmware
 * side and are packed (use only of 32 bits size fields in messages
 * structures to ensure packing):
 * - struct delta_ipc_open_msg
 * - struct delta_ipc_set_stream_msg
 * - struct delta_ipc_decode_msg
 * - struct delta_ipc_close_msg
 * - struct delta_ipc_cb_msg
 */
struct delta_ipc_open_msg {};

struct delta_ipc_set_stream_msg {};

struct delta_ipc_decode_msg {};

struct delta_ipc_close_msg {};

struct delta_ipc_cb_msg {};

static void build_msg_header(struct delta_ipc_ctx *ctx,
			     enum delta_ipc_fw_command command,
			     struct delta_ipc_header_msg *header)
{}

int delta_ipc_open(struct delta_ctx *pctx, const char *name,
		   struct delta_ipc_param *param, u32 ipc_buf_size,
		   struct delta_buf **ipc_buf, void **hdl)
{
	struct delta_dev *delta = pctx->dev;
	struct rpmsg_device *rpmsg_device = delta->rpmsg_device;
	struct delta_ipc_ctx *ctx = &pctx->ipc_ctx;
	struct delta_ipc_open_msg msg;
	struct delta_buf *buf = &ctx->ipc_buf_struct;
	int ret;

	if (!rpmsg_device) {
		dev_err(delta->dev,
			"%s   ipc: failed to open, rpmsg is not initialized\n",
			pctx->name);
		pctx->sys_errors++;
		return -EINVAL;
	}

	if (!name) {
		dev_err(delta->dev,
			"%s   ipc: failed to open, no name given\n",
			pctx->name);
		return -EINVAL;
	}

	if (!param || !param->data || !param->size) {
		dev_err(delta->dev,
			"%s  ipc: failed to open, empty parameter\n",
			pctx->name);
		return -EINVAL;
	}

	if (!ipc_buf_size) {
		dev_err(delta->dev,
			"%s   ipc: failed to open, no size given for ipc buffer\n",
			pctx->name);
		return -EINVAL;
	}

	if (param->size > ipc_buf_size) {
		dev_err(delta->dev,
			"%s   ipc: failed to open, too large ipc parameter (%d bytes while max %d expected)\n",
			pctx->name,
			param->size, ctx->ipc_buf->size);
		return -EINVAL;
	}

	/* init */
	init_completion(&ctx->done);

	/*
	 * allocation of contiguous buffer for
	 * data of commands exchanged between
	 * host and firmware coprocessor
	 */
	ret = hw_alloc(pctx, ipc_buf_size,
		       "ipc data buffer", buf);
	if (ret)
		return ret;
	ctx->ipc_buf = buf;

	/* build rpmsg message */
	build_msg_header(ctx, DELTA_IPC_OPEN, &msg.header);

	msg.ipc_buf_size = ipc_buf_size;
	msg.ipc_buf_paddr = ctx->ipc_buf->paddr;

	strscpy(msg.name, name, sizeof(msg.name));

	msg.param_size = param->size;
	memcpy(ctx->ipc_buf->vaddr, param->data, msg.param_size);
	msg.param_paddr = ctx->ipc_buf->paddr;

	/* send it */
	ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));
	if (ret) {
		dev_err(delta->dev,
			"%s   ipc: failed to open, rpmsg_send failed (%d) for DELTA_IPC_OPEN (name=%s, size=%d, data=%p)\n",
			pctx->name,
			ret, name, param->size, param->data);
		goto err;
	}

	/* wait for acknowledge */
	if (!wait_for_completion_timeout
	    (&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {
		dev_err(delta->dev,
			"%s   ipc: failed to open, timeout waiting for DELTA_IPC_OPEN callback (name=%s, size=%d, data=%p)\n",
			pctx->name,
			name, param->size, param->data);
		ret = -ETIMEDOUT;
		goto err;
	}

	/* command completed, check error */
	if (ctx->cb_err) {
		dev_err(delta->dev,
			"%s   ipc: failed to open, DELTA_IPC_OPEN completed but with error (%d) (name=%s, size=%d, data=%p)\n",
			pctx->name,
			ctx->cb_err, name, param->size, param->data);
		ret = -EIO;
		goto err;
	}

	*ipc_buf = ctx->ipc_buf;
	*hdl = (void *)ctx;

	return 0;

err:
	pctx->sys_errors++;
	hw_free(pctx, ctx->ipc_buf);
	ctx->ipc_buf = NULL;

	return ret;
};

int delta_ipc_set_stream(void *hdl, struct delta_ipc_param *param)
{}

int delta_ipc_decode(void *hdl, struct delta_ipc_param *param,
		     struct delta_ipc_param *status)
{
	struct delta_ipc_ctx *ctx = to_ctx(hdl);
	struct delta_ctx *pctx = to_pctx(ctx);
	struct delta_dev *delta = pctx->dev;
	struct rpmsg_device *rpmsg_device = delta->rpmsg_device;
	struct delta_ipc_decode_msg msg;
	int ret;

	if (!hdl) {
		dev_err(delta->dev,
			"%s   ipc: failed to decode, invalid ipc handle\n",
			pctx->name);
		return -EINVAL;
	}

	if (!rpmsg_device) {
		dev_err(delta->dev,
			"%s   ipc: failed to decode, rpmsg is not initialized\n",
			pctx->name);
		return -EINVAL;
	}

	if (!param || !param->data || !param->size) {
		dev_err(delta->dev,
			"%s  ipc: failed to decode, empty parameter\n",
			pctx->name);
		return -EINVAL;
	}

	if (!status || !status->data || !status->size) {
		dev_err(delta->dev,
			"%s  ipc: failed to decode, empty status\n",
			pctx->name);
		return -EINVAL;
	}

	if (param->size + status->size > ctx->ipc_buf->size) {
		dev_err(delta->dev,
			"%s   ipc: failed to decode, too large ipc parameter (%d bytes (param) + %d bytes (status) while max %d expected)\n",
			pctx->name,
			param->size,
			status->size,
			ctx->ipc_buf->size);
		return -EINVAL;
	}

	if (!is_valid_data(ctx, param->data, param->size)) {
		dev_err(delta->dev,
			"%s   ipc: failed to decode, parameter is not in expected address range (size=%d, data=%p not in %p..%p)\n",
			pctx->name,
			param->size,
			param->data,
			ctx->ipc_buf->vaddr,
			ctx->ipc_buf->vaddr + ctx->ipc_buf->size - 1);
		return -EINVAL;
	}

	if (!is_valid_data(ctx, status->data, status->size)) {
		dev_err(delta->dev,
			"%s   ipc: failed to decode, status is not in expected address range (size=%d, data=%p not in %p..%p)\n",
			pctx->name,
			status->size,
			status->data,
			ctx->ipc_buf->vaddr,
			ctx->ipc_buf->vaddr + ctx->ipc_buf->size - 1);
		return -EINVAL;
	}

	/* build rpmsg message */
	build_msg_header(ctx, DELTA_IPC_DECODE, &msg.header);

	msg.param_size = param->size;
	msg.param_paddr = to_paddr(ctx, param->data);

	msg.status_size = status->size;
	msg.status_paddr = to_paddr(ctx, status->data);

	/* send it */
	ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));
	if (ret) {
		dev_err(delta->dev,
			"%s   ipc: failed to decode, rpmsg_send failed (%d) for DELTA_IPC_DECODE (size=%d, data=%p)\n",
			pctx->name,
			ret, param->size, param->data);
		pctx->sys_errors++;
		return ret;
	}

	/* wait for acknowledge */
	if (!wait_for_completion_timeout
	    (&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {
		dev_err(delta->dev,
			"%s   ipc: failed to decode, timeout waiting for DELTA_IPC_DECODE callback (size=%d, data=%p)\n",
			pctx->name,
			param->size, param->data);
		pctx->sys_errors++;
		return -ETIMEDOUT;
	}

	/* command completed, check status */
	if (ctx->cb_err) {
		dev_err(delta->dev,
			"%s   ipc: failed to decode, DELTA_IPC_DECODE completed but with error (%d) (size=%d, data=%p)\n",
			pctx->name,
			ctx->cb_err, param->size, param->data);
		pctx->sys_errors++;
		return -EIO;
	}

	return 0;
};

void delta_ipc_close(void *hdl)
{
	struct delta_ipc_ctx *ctx = to_ctx(hdl);
	struct delta_ctx *pctx = to_pctx(ctx);
	struct delta_dev *delta = pctx->dev;
	struct rpmsg_device *rpmsg_device = delta->rpmsg_device;
	struct delta_ipc_close_msg msg;
	int ret;

	if (!hdl) {
		dev_err(delta->dev,
			"%s   ipc: failed to close, invalid ipc handle\n",
			pctx->name);
		return;
	}

	if (ctx->ipc_buf) {
		hw_free(pctx, ctx->ipc_buf);
		ctx->ipc_buf = NULL;
	}

	if (!rpmsg_device) {
		dev_err(delta->dev,
			"%s   ipc: failed to close, rpmsg is not initialized\n",
			pctx->name);
		return;
	}

	/* build rpmsg message */
	build_msg_header(ctx, DELTA_IPC_CLOSE, &msg.header);

	/* send it */
	ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));
	if (ret) {
		dev_err(delta->dev,
			"%s   ipc: failed to close, rpmsg_send failed (%d) for DELTA_IPC_CLOSE\n",
			pctx->name, ret);
		pctx->sys_errors++;
		return;
	}

	/* wait for acknowledge */
	if (!wait_for_completion_timeout
	    (&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {
		dev_err(delta->dev,
			"%s   ipc: failed to close, timeout waiting for DELTA_IPC_CLOSE callback\n",
			pctx->name);
		pctx->sys_errors++;
		return;
	}

	/* command completed, check status */
	if (ctx->cb_err) {
		dev_err(delta->dev,
			"%s   ipc: failed to close, DELTA_IPC_CLOSE completed but with error (%d)\n",
			pctx->name, ctx->cb_err);
		pctx->sys_errors++;
	}
};

static int delta_ipc_cb(struct rpmsg_device *rpdev, void *data,
			int len, void *priv, u32 src)
{}

static int delta_ipc_probe(struct rpmsg_device *rpmsg_device)
{}

static void delta_ipc_remove(struct rpmsg_device *rpmsg_device)
{}

static struct rpmsg_device_id delta_ipc_device_id_table[] =;

static struct rpmsg_driver delta_rpmsg_driver =;

int delta_ipc_init(struct delta_dev *delta)
{}

void delta_ipc_exit(struct delta_dev *delta)
{}