smmu && dmabuf

/*

  • A virtual v4l2-mem2mem example device.
  • This is a virtual device driver for testing mem-to-mem videobuf framework.
  • It simulates a device that uses memory buffers for both source and
  • destination, processes the data and issues an "irq" (simulated by a delayed
  • workqueue).
  • The device is capable of multi-instance, multi-buffer-per-transaction
  • operation (via the mem2mem framework).
  • Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
  • Pawel Osciak, pawel@osciak.com
  • Marek Szyprowski, m.szyprowski@samsung.com
  • This program is free software; you can redistribute it and/or modify
  • it under the terms of the GNU General Public License as published by the
  • Free Software Foundation; either version 2 of the
  • License, or (at your option) any later version
    */

include <linux/module.h>

include <linux/delay.h>

include <linux/fs.h>

include <linux/sched.h>

include <linux/slab.h>

include <linux/platform_device.h>

include <media/v4l2-mem2mem.h>

include <media/v4l2-device.h>

include <media/v4l2-ioctl.h>

include <media/v4l2-ctrls.h>

include <media/v4l2-event.h>

include <media/videobuf2-vmalloc.h>

include <media/videobuf2-dma-contig.h>

struct device *global_dev;

MODULE_DESCRIPTION("Virtual device for mem2mem framework testing");
MODULE_AUTHOR("Pawel Osciak, pawel@osciak.com");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1.1");
MODULE_ALIAS("mem2mem_testdev");

static unsigned debug;
module_param(debug, uint, 0644);
MODULE_PARM_DESC(debug, "activates debug info");

define MIN_W 32

define MIN_H 32

define MAX_W 640

define MAX_H 480

define DIM_ALIGN_MASK 7 /* 8-byte alignment for line length */

/* Flags that indicate a format can be used for capture/output */

define MEM2MEM_CAPTURE (1 << 0)

define MEM2MEM_OUTPUT (1 << 1)

define MEM2MEM_NAME "vim2m"

/* Per queue */

define MEM2MEM_DEF_NUM_BUFS VIDEO_MAX_FRAME

/* In bytes, per queue */

define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024)

/* Default transaction time in msec */

define MEM2MEM_DEF_TRANSTIME 40

define MEM2MEM_COLOR_STEP (0xff >> 4)

define MEM2MEM_NUM_TILES 8

/* Flags that indicate processing mode */

define MEM2MEM_HFLIP (1 << 0)

define MEM2MEM_VFLIP (1 << 1)

define dprintk(dev, fmt, arg...) \

v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)

static void vim2m_dev_release(struct device *dev)
{}

static struct platform_device vim2m_pdev = {
.name = MEM2MEM_NAME,
.dev.release = vim2m_dev_release,
};

struct vim2m_fmt {
u32 fourcc;
int depth;
/* Types the format can be used for */
u32 types;
};

static struct vim2m_fmt formats[] = {
{
.fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb /
.depth = 16,
/
Both capture and output format /
.types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
},
{
.fourcc = V4L2_PIX_FMT_YUYV,
.depth = 16,
/
Output-only format */
.types = MEM2MEM_OUTPUT,
},
};

define NUM_FORMATS ARRAY_SIZE(formats)

/* Per-queue, driver-specific private data /
struct vim2m_q_data {
unsigned int width;
unsigned int height;
unsigned int sizeimage; /
多大内存 */
unsigned int sequence;
struct vim2m_fmt *fmt;
};

enum {
V4L2_M2M_SRC = 0,
V4L2_M2M_DST = 1,
};

define V4L2_CID_TRANS_TIME_MSEC (V4L2_CID_USER_BASE + 0x1000)

define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_USER_BASE + 0x1001)

static struct vim2m_fmt *find_format(struct v4l2_format *f)
{
struct vim2m_fmt *fmt;
unsigned int k;

for (k = 0; k < NUM_FORMATS; k++) {
	fmt = &formats[k];
	if (fmt->fourcc == f->fmt.pix.pixelformat)	/* 通过比较fourcc,找到对应的格式 */
		break;
}

if (k == NUM_FORMATS)
	return NULL;

return &formats[k];

}

struct vim2m_dev {
struct v4l2_device v4l2_dev;
struct video_device vfd;

ifdef CONFIG_MEDIA_CONTROLLER

struct media_device	mdev;

endif

atomic_t		num_inst;
struct mutex		dev_mutex;
spinlock_t		irqlock;

struct delayed_work	work_run;

struct v4l2_m2m_dev	*m2m_dev;

};

struct vim2m_ctx {
struct v4l2_fh fh;
struct vim2m_dev *dev;

struct v4l2_ctrl_handler hdl;

/* Processed buffers in this transaction */
u8			num_processed;

/* Transaction length (i.e. how many buffers per transaction) */
u32			translen;
/* Transaction time (i.e. simulated processing time) in milliseconds */
u32			transtime;

/* Abort requested by m2m */
int			aborting;

/* Processing mode */
int			mode;

enum v4l2_colorspace	colorspace;
enum v4l2_ycbcr_encoding ycbcr_enc;
enum v4l2_xfer_func	xfer_func;
enum v4l2_quantization	quant;

/* Source and destination queue data */
struct vim2m_q_data   q_data[2];	/* 存放分辨率相关信息 */

};

static inline struct vim2m_ctx *file2ctx(struct file *file)
{
return container_of(file->private_data, struct vim2m_ctx, fh);
}

static struct vim2m_q_data *get_q_data(struct vim2m_ctx *ctx,
enum v4l2_buf_type type)
{
switch (type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
return &ctx->q_data[V4L2_M2M_SRC];
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
return &ctx->q_data[V4L2_M2M_DST];
default:
BUG();
}
return NULL;
}

static int device_process(struct vim2m_ctx *ctx,
struct vb2_v4l2_buffer *in_vb,
struct vb2_v4l2_buffer *out_vb)
{
struct vim2m_dev *dev = ctx->dev;
struct vim2m_q_data *q_data;
u8 *p_in, *p_out;
int x, y, t, w;
int tile_w, bytes_left;
int width, height, bytesperline;

q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);

width	= q_data->width;
height	= q_data->height;
bytesperline	= (q_data->width * q_data->fmt->depth) >> 3;

p_in = vb2_plane_vaddr(&in_vb->vb2_buf, 0);
p_out = vb2_plane_vaddr(&out_vb->vb2_buf, 0);
if (!p_in || !p_out) {
	v4l2_err(&dev->v4l2_dev,
		 "Acquiring kernel pointers to buffers failed\n");
	return -EFAULT;
}

if (vb2_plane_size(&in_vb->vb2_buf, 0) >
		vb2_plane_size(&out_vb->vb2_buf, 0)) {
	v4l2_err(&dev->v4l2_dev, "Output buffer is too small\n");
	return -EINVAL;
}

tile_w = (width * (q_data[V4L2_M2M_DST].fmt->depth >> 3))
	/ MEM2MEM_NUM_TILES;
bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES;
w = 0;

out_vb->sequence =
	get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++;
in_vb->sequence = q_data->sequence++;
out_vb->vb2_buf.timestamp = in_vb->vb2_buf.timestamp;

if (in_vb->flags & V4L2_BUF_FLAG_TIMECODE)
	out_vb->timecode = in_vb->timecode;
out_vb->field = in_vb->field;
out_vb->flags = in_vb->flags &
	(V4L2_BUF_FLAG_TIMECODE |
	 V4L2_BUF_FLAG_KEYFRAME |
	 V4L2_BUF_FLAG_PFRAME |
	 V4L2_BUF_FLAG_BFRAME |
	 V4L2_BUF_FLAG_TSTAMP_SRC_MASK);

switch (ctx->mode) {
case MEM2MEM_HFLIP | MEM2MEM_VFLIP:
	p_out += bytesperline * height - bytes_left;
	for (y = 0; y < height; ++y) {
		for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
			if (w & 0x1) {
				for (x = 0; x < tile_w; ++x)
					*--p_out = *p_in++ +
						MEM2MEM_COLOR_STEP;
			} else {
				for (x = 0; x < tile_w; ++x)
					*--p_out = *p_in++ -
						MEM2MEM_COLOR_STEP;
			}
			++w;
		}
		p_in += bytes_left;
		p_out -= bytes_left;
	}
	break;

case MEM2MEM_HFLIP:
	for (y = 0; y < height; ++y) {
		p_out += MEM2MEM_NUM_TILES * tile_w;
		for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
			if (w & 0x01) {
				for (x = 0; x < tile_w; ++x)
					*--p_out = *p_in++ +
						MEM2MEM_COLOR_STEP;
			} else {
				for (x = 0; x < tile_w; ++x)
					*--p_out = *p_in++ -
						MEM2MEM_COLOR_STEP;
			}
			++w;
		}
		p_in += bytes_left;
		p_out += bytesperline;
	}
	break;

case MEM2MEM_VFLIP:
	p_out += bytesperline * (height - 1);
	for (y = 0; y < height; ++y) {
		for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
			if (w & 0x1) {
				for (x = 0; x < tile_w; ++x)
					*p_out++ = *p_in++ +
						MEM2MEM_COLOR_STEP;
			} else {
				for (x = 0; x < tile_w; ++x)
					*p_out++ = *p_in++ -
						MEM2MEM_COLOR_STEP;
			}
			++w;
		}
		p_in += bytes_left;
		p_out += bytes_left - 2 * bytesperline;
	}
	break;

default:
	for (y = 0; y < height; ++y) {
		for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
			if (w & 0x1) {
				for (x = 0; x < tile_w; ++x)
					*p_out++ = *p_in++ +
						MEM2MEM_COLOR_STEP;
			} else {
				for (x = 0; x < tile_w; ++x)
					*p_out++ = *p_in++ -
						MEM2MEM_COLOR_STEP;
			}
			++w;
		}
		p_in += bytes_left;
		p_out += bytes_left;
	}
}

return 0;

}

/*

  • mem2mem callbacks
    */

/*

  • job_ready() - check whether an instance is ready to be scheduled to run
    */
    static int job_ready(void *priv)
    {
    struct vim2m_ctx *ctx = priv;

    if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen
    || v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen) {
    dprintk(ctx->dev, "Not enough buffers available\n");
    return 0;
    }

    return 1;
    }

static void job_abort(void *priv)
{
struct vim2m_ctx *ctx = priv;

/* Will cancel the transaction in the next interrupt handler */
ctx->aborting = 1;

}

/* device_run() - prepares and starts the device
*

  • This simulates all the immediate preparations required before starting

  • a device. This will be called by the framework when it decides to schedule

  • a particular instance.
    */
    static void device_run(void *priv)
    {
    struct vim2m_ctx *ctx = priv;
    struct vim2m_dev *dev = ctx->dev;
    struct vb2_v4l2_buffer *src_buf, *dst_buf;

    src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
    dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);

    /* Apply request controls if any */
    v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req,
    &ctx->hdl);

    device_process(ctx, src_buf, dst_buf);

    /* Complete request controls if any */
    v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req,
    &ctx->hdl);

    /* Run delayed work, which simulates a hardware irq */
    schedule_delayed_work(&dev->work_run, msecs_to_jiffies(ctx->transtime));
    }

static void device_work(struct work_struct *w)
{
struct vim2m_dev *vim2m_dev =
container_of(w, struct vim2m_dev, work_run.work);
struct vim2m_ctx *curr_ctx;
struct vb2_v4l2_buffer *src_vb, *dst_vb;
unsigned long flags;

curr_ctx = v4l2_m2m_get_curr_priv(vim2m_dev->m2m_dev);

if (NULL == curr_ctx) {
	pr_err("Instance released before the end of transaction\n");
	return;
}

src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);

curr_ctx->num_processed++;

spin_lock_irqsave(&vim2m_dev->irqlock, flags);
v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
spin_unlock_irqrestore(&vim2m_dev->irqlock, flags);

if (curr_ctx->num_processed == curr_ctx->translen
    || curr_ctx->aborting) {
	dprintk(curr_ctx->dev, "Finishing transaction\n");
	curr_ctx->num_processed = 0;
	v4l2_m2m_job_finish(vim2m_dev->m2m_dev, curr_ctx->fh.m2m_ctx);
} else {
	device_run(curr_ctx);
}

}

/*

  • video ioctls
    */
    static int vidioc_querycap(struct file *file, void *priv,
    struct v4l2_capability *cap)
    {
    strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
    strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
    snprintf(cap->bus_info, sizeof(cap->bus_info),
    "platform:%s", MEM2MEM_NAME);
    return 0;
    }

static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
{
int i, num;
struct vim2m_fmt *fmt;

num = 0;

for (i = 0; i < NUM_FORMATS; ++i) {
	if (formats[i].types & type) {
		/* index-th format of type type found ? */
		if (num == f->index)
			break;
		/* Correct type but haven't reached our index yet,
		 * just increment per-type index */
		++num;
	}
}

if (i < NUM_FORMATS) {
	/* Format found */
	fmt = &formats[i];
	f->pixelformat = fmt->fourcc;
	return 0;
}

/* Format not found */
return -EINVAL;

}

static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
return enum_fmt(f, MEM2MEM_CAPTURE);
}

static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
return enum_fmt(f, MEM2MEM_OUTPUT);
}

static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f)
{
struct vb2_queue *vq;
struct vim2m_q_data *q_data;

vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
if (!vq)
	return -EINVAL;

q_data = get_q_data(ctx, f->type);

f->fmt.pix.width	= q_data->width;
f->fmt.pix.height	= q_data->height;
f->fmt.pix.field	= V4L2_FIELD_NONE;
f->fmt.pix.pixelformat	= q_data->fmt->fourcc;
f->fmt.pix.bytesperline	= (q_data->width * q_data->fmt->depth) >> 3;
f->fmt.pix.sizeimage	= q_data->sizeimage;
f->fmt.pix.colorspace	= ctx->colorspace;
f->fmt.pix.xfer_func	= ctx->xfer_func;
f->fmt.pix.ycbcr_enc	= ctx->ycbcr_enc;
f->fmt.pix.quantization	= ctx->quant;

return 0;

}

static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
return vidioc_g_fmt(file2ctx(file), f);
}

static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
return vidioc_g_fmt(file2ctx(file), f);
}

static int vidioc_try_fmt(struct v4l2_format *f, struct vim2m_fmt fmt)
{
/
V4L2 specification suggests the driver corrects the format struct
* if any of the dimensions is unsupported */
if (f->fmt.pix.height < MIN_H)
f->fmt.pix.height = MIN_H;
else if (f->fmt.pix.height > MAX_H)
f->fmt.pix.height = MAX_H;

if (f->fmt.pix.width < MIN_W)
	f->fmt.pix.width = MIN_W;
else if (f->fmt.pix.width > MAX_W)
	f->fmt.pix.width = MAX_W;

f->fmt.pix.width &= ~DIM_ALIGN_MASK;
f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
f->fmt.pix.field = V4L2_FIELD_NONE;

return 0;

}

static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vim2m_fmt *fmt;
struct vim2m_ctx ctx = file2ctx(file); / 找到对应的ctx */

fmt = find_format(f);
if (!fmt) {
	f->fmt.pix.pixelformat = formats[0].fourcc;
	fmt = find_format(f);
}
if (!(fmt->types & MEM2MEM_CAPTURE)) {
	v4l2_err(&ctx->dev->v4l2_dev,
		 "Fourcc format (0x%08x) invalid.\n",
		 f->fmt.pix.pixelformat);
	return -EINVAL;
}
f->fmt.pix.colorspace = ctx->colorspace;
f->fmt.pix.xfer_func = ctx->xfer_func;
f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc;
f->fmt.pix.quantization = ctx->quant;

return vidioc_try_fmt(f, fmt);

}

static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vim2m_fmt *fmt;
struct vim2m_ctx *ctx = file2ctx(file);

fmt = find_format(f);
if (!fmt) {
	f->fmt.pix.pixelformat = formats[0].fourcc;
	fmt = find_format(f);
}
if (!(fmt->types & MEM2MEM_OUTPUT)) {
	v4l2_err(&ctx->dev->v4l2_dev,
		 "Fourcc format (0x%08x) invalid.\n",
		 f->fmt.pix.pixelformat);
	return -EINVAL;
}
if (!f->fmt.pix.colorspace)
	f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;

return vidioc_try_fmt(f, fmt);

}

static int vidioc_s_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f)
{
struct vim2m_q_data *q_data;
struct vb2_queue *vq;

vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);	/* 根据type,得到对应的vb2_queue */
if (!vq)
	return -EINVAL;

q_data = get_q_data(ctx, f->type);
if (!q_data)
	return -EINVAL;

if (vb2_is_busy(vq)) {
	v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
	return -EBUSY;
}

q_data->fmt		= find_format(f);
q_data->width		= f->fmt.pix.width;
q_data->height		= f->fmt.pix.height;
q_data->sizeimage	= q_data->width * q_data->height
			* q_data->fmt->depth >> 3;	/* 设置格式 */

dprintk(ctx->dev,
	"Setting format for type %d, wxh: %dx%d, fmt: %d\n",
	f->type, q_data->width, q_data->height, q_data->fmt->fourcc);

return 0;

}

static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
int ret;

ret = vidioc_try_fmt_vid_cap(file, priv, f);	/* 对应用层的参数做一些检验,不满足要求的进行重新赋值 */
if (ret)
	return ret;

return vidioc_s_fmt(file2ctx(file), f);

}

static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vim2m_ctx *ctx = file2ctx(file);
int ret;

ret = vidioc_try_fmt_vid_out(file, priv, f);
if (ret)
	return ret;

ret = vidioc_s_fmt(file2ctx(file), f);
if (!ret) {
	ctx->colorspace = f->fmt.pix.colorspace;
	ctx->xfer_func = f->fmt.pix.xfer_func;
	ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc;
	ctx->quant = f->fmt.pix.quantization;
}
return ret;

}

static int vim2m_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct vim2m_ctx *ctx =
container_of(ctrl->handler, struct vim2m_ctx, hdl);

switch (ctrl->id) {
case V4L2_CID_HFLIP:
	if (ctrl->val)
		ctx->mode |= MEM2MEM_HFLIP;
	else
		ctx->mode &= ~MEM2MEM_HFLIP;
	break;

case V4L2_CID_VFLIP:
	if (ctrl->val)
		ctx->mode |= MEM2MEM_VFLIP;
	else
		ctx->mode &= ~MEM2MEM_VFLIP;
	break;

case V4L2_CID_TRANS_TIME_MSEC:
	ctx->transtime = ctrl->val;
	break;

case V4L2_CID_TRANS_NUM_BUFS:
	ctx->translen = ctrl->val;
	break;

default:
	v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
	return -EINVAL;
}

return 0;

}

static const struct v4l2_ctrl_ops vim2m_ctrl_ops = {
.s_ctrl = vim2m_s_ctrl,
};

static const struct v4l2_ioctl_ops vim2m_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
/* format */
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,

.vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
.vidioc_g_fmt_vid_out	= vidioc_g_fmt_vid_out,
.vidioc_try_fmt_vid_out	= vidioc_try_fmt_vid_out,
.vidioc_s_fmt_vid_out	= vidioc_s_fmt_vid_out,
/* buffer */
.vidioc_reqbufs		= v4l2_m2m_ioctl_reqbufs,
.vidioc_querybuf	= v4l2_m2m_ioctl_querybuf,
.vidioc_qbuf		= v4l2_m2m_ioctl_qbuf,
.vidioc_dqbuf	= v4l2_m2m_ioctl_dqbuf,
.vidioc_prepare_buf	= v4l2_m2m_ioctl_prepare_buf,
.vidioc_create_bufs	= v4l2_m2m_ioctl_create_bufs,
.vidioc_expbuf		= v4l2_m2m_ioctl_expbuf,

.vidioc_streamon	= v4l2_m2m_ioctl_streamon,
.vidioc_streamoff	= v4l2_m2m_ioctl_streamoff,

.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,	

};

/*

  • Queue operations
    */

static int vim2m_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], struct device *alloc_devs[])
{
struct vim2m_ctx *ctx = vb2_get_drv_priv(vq);
struct vim2m_q_data *q_data;
unsigned int size, count = *nbuffers;

q_data = get_q_data(ctx, vq->type);

size = q_data->width * q_data->height * q_data->fmt->depth >> 3;

while (size * count > MEM2MEM_VID_MEM_LIMIT)
	(count)--;
*nbuffers = count;

if (*nplanes)
	return sizes[0] < size ? -EINVAL : 0;

*nplanes = 1;	/* plane数量 */
sizes[0] = size;	/* 每个plan的size */

alloc_devs[0] = global_dev;
dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size);

return 0;

}

static int vim2m_buf_prepare(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct vim2m_q_data *q_data;

dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);

q_data = get_q_data(ctx, vb->vb2_queue->type);
if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
	if (vbuf->field == V4L2_FIELD_ANY)
		vbuf->field = V4L2_FIELD_NONE;
	if (vbuf->field != V4L2_FIELD_NONE) {
		dprintk(ctx->dev, "%s field isn't supported\n",
				__func__);
		return -EINVAL;
	}
}

if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
	dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n",
			__func__, vb2_plane_size(vb, 0), (long)q_data->sizeimage);
	return -EINVAL;
}

vb2_set_plane_payload(vb, 0, q_data->sizeimage);

return 0;

}

static void vim2m_buf_queue(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);

v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);

}

static int vim2m_start_streaming(struct vb2_queue *q, unsigned count)
{
struct vim2m_ctx *ctx = vb2_get_drv_priv(q);
struct vim2m_q_data *q_data = get_q_data(ctx, q->type);

q_data->sequence = 0;
return 0;

}

static void vim2m_stop_streaming(struct vb2_queue *q)
{
struct vim2m_ctx *ctx = vb2_get_drv_priv(q);
struct vim2m_dev *dev = ctx->dev;
struct vb2_v4l2_buffer *vbuf;
unsigned long flags;

if (v4l2_m2m_get_curr_priv(dev->m2m_dev) == ctx)	/* 如果该ctx还在处理 */
	cancel_delayed_work_sync(&dev->work_run);

for (;;) {
	if (V4L2_TYPE_IS_OUTPUT(q->type))
		vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
	else
		vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
	if (vbuf == NULL)	/* 如果没有buffer */
		return;	
	v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
				   &ctx->hdl);
	spin_lock_irqsave(&ctx->dev->irqlock, flags);
	v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
	spin_unlock_irqrestore(&ctx->dev->irqlock, flags);
}

}

static void vim2m_buf_request_complete(struct vb2_buffer *vb)
{
struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);

v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);

}

static const struct vb2_ops vim2m_qops = {
.queue_setup = vim2m_queue_setup,
.buf_prepare = vim2m_buf_prepare,
.buf_queue = vim2m_buf_queue,
.start_streaming = vim2m_start_streaming,
.stop_streaming = vim2m_stop_streaming,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
.buf_request_complete = vim2m_buf_request_complete,
};

static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
{
struct vim2m_ctx *ctx = priv;
int ret;

src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;	/* 设置type */
src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
src_vq->drv_priv = ctx;	/* 设置私有数据 */
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->ops = &vim2m_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
src_vq->lock = &ctx->dev->dev_mutex;
src_vq->supports_requests = true;

ret = vb2_queue_init(src_vq);
if (ret)
	return ret;

dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;	/* 设置type */
dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
dst_vq->drv_priv = ctx;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->ops = &vim2m_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
dst_vq->lock = &ctx->dev->dev_mutex;

return vb2_queue_init(dst_vq);

}

static const struct v4l2_ctrl_config vim2m_ctrl_trans_time_msec = {
.ops = &vim2m_ctrl_ops,
.id = V4L2_CID_TRANS_TIME_MSEC,
.name = "Transaction Time (msec)",
.type = V4L2_CTRL_TYPE_INTEGER,
.def = MEM2MEM_DEF_TRANSTIME,
.min = 1,
.max = 10001,
.step = 1,
};

static const struct v4l2_ctrl_config vim2m_ctrl_trans_num_bufs = {
.ops = &vim2m_ctrl_ops,
.id = V4L2_CID_TRANS_NUM_BUFS,
.name = "Buffers Per Transaction",
.type = V4L2_CTRL_TYPE_INTEGER,
.def = 1,
.min = 1,
.max = MEM2MEM_DEF_NUM_BUFS,
.step = 1,
};

/*

  • File operations
    */
    static int vim2m_open(struct file *file)
    {
    struct vim2m_dev dev = video_drvdata(file); / dev */
    struct vim2m_ctx ctx = NULL; / ctx */
    struct v4l2_ctrl_handler *hdl;
    int rc = 0;

    if (mutex_lock_interruptible(&dev->dev_mutex))
    return -ERESTARTSYS;
    ctx = kzalloc(sizeof(ctx), GFP_KERNEL); / 每一次open创建一次ctx,整个系统只在probe的时候创建dev,就可以实现多实例支持; */
    if (!ctx) {
    rc = -ENOMEM;
    goto open_unlock;
    }

    v4l2_fh_init(&ctx->fh, video_devdata(file));
    file->private_data = &ctx->fh;
    ctx->dev = dev;
    hdl = &ctx->hdl;
    v4l2_ctrl_handler_init(hdl, 4);
    v4l2_ctrl_new_std(hdl, &vim2m_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
    v4l2_ctrl_new_std(hdl, &vim2m_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
    v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_time_msec, NULL);
    v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_num_bufs, NULL);
    if (hdl->error) {
    rc = hdl->error;
    v4l2_ctrl_handler_free(hdl);
    kfree(ctx);
    goto open_unlock;
    }
    ctx->fh.ctrl_handler = hdl;
    v4l2_ctrl_handler_setup(hdl);

    ctx->q_data[V4L2_M2M_SRC].fmt = &formats[0];
    ctx->q_data[V4L2_M2M_SRC].width = 640;
    ctx->q_data[V4L2_M2M_SRC].height = 480;
    ctx->q_data[V4L2_M2M_SRC].sizeimage =
    ctx->q_data[V4L2_M2M_SRC].width *
    ctx->q_data[V4L2_M2M_SRC].height *
    (ctx->q_data[V4L2_M2M_SRC].fmt->depth >> 3);
    ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC];
    ctx->colorspace = V4L2_COLORSPACE_REC709;

    ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); /* 指定m2m_ctx */

    if (IS_ERR(ctx->fh.m2m_ctx)) {
    rc = PTR_ERR(ctx->fh.m2m_ctx);

     v4l2_ctrl_handler_free(hdl);
     v4l2_fh_exit(&ctx->fh);
     kfree(ctx);
     goto open_unlock;
    

    }

    v4l2_fh_add(&ctx->fh);
    atomic_inc(&dev->num_inst);

    dprintk(dev, "Created instance: %p, m2m_ctx: %p\n",
    ctx, ctx->fh.m2m_ctx);

open_unlock:
mutex_unlock(&dev->dev_mutex);
return rc;
}

static int vim2m_release(struct file *file)
{
struct vim2m_dev *dev = video_drvdata(file);
struct vim2m_ctx *ctx = file2ctx(file);

dprintk(dev, "Releasing instance %p\n", ctx);

v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
v4l2_ctrl_handler_free(&ctx->hdl);
mutex_lock(&dev->dev_mutex);
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
mutex_unlock(&dev->dev_mutex);
kfree(ctx);

atomic_dec(&dev->num_inst);

return 0;

}

static const struct v4l2_file_operations vim2m_fops = {
.owner = THIS_MODULE,
.open = vim2m_open,
.release = vim2m_release,
.poll = v4l2_m2m_fop_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = v4l2_m2m_fop_mmap,
};

static const struct video_device vim2m_videodev = {
.name = MEM2MEM_NAME,
.vfl_dir = VFL_DIR_M2M,
.fops = &vim2m_fops,
.ioctl_ops = &vim2m_ioctl_ops,
.minor = -1,
.release = video_device_release_empty,
.device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
};

static const struct v4l2_m2m_ops m2m_ops = {
.device_run = device_run,
.job_ready = job_ready,
.job_abort = job_abort,
};

static const struct media_device_ops m2m_media_ops = {
.req_validate = vb2_request_validate,
.req_queue = v4l2_m2m_request_queue,
};

static int vim2m_probe(struct platform_device *pdev)
{
struct vim2m_dev *dev;
struct video_device *vfd;
int ret;

dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);	/* 分配dev内存 */
if (!dev)
	return -ENOMEM;

spin_lock_init(&dev->irqlock);

ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret)
	return ret;
global_dev = &pdev->dev;
atomic_set(&dev->num_inst, 0);
mutex_init(&dev->dev_mutex);

// pdev->dev.dma_mask = DMA_BIT_MASK(32);
dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));

dev->vfd = vim2m_videodev;	/* 指定video结点 */
vfd = &dev->vfd;
vfd->lock = &dev->dev_mutex;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->dev_debug = (V4L2_DEV_DEBUG_IOCTL | V4L2_DEV_DEBUG_IOCTL_ARG | V4L2_DEV_DEBUG_FOP | V4L2_DEV_DEBUG_STREAMING | V4L2_DEV_DEBUG_POLL);
INIT_DELAYED_WORK(&dev->work_run, device_work);

ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
if (ret) {
	v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
	goto unreg_v4l2;
}

video_set_drvdata(vfd, dev);	/* 设置driver data */
v4l2_info(&dev->v4l2_dev,
		"Device registered as /dev/video%d\n", vfd->num);

platform_set_drvdata(pdev, dev);

dev->m2m_dev = v4l2_m2m_init(&m2m_ops);	/* 初始化v4l2_m2m_dev */
if (IS_ERR(dev->m2m_dev)) {
	v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
	ret = PTR_ERR(dev->m2m_dev);
	goto unreg_dev;
}

ifdef CONFIG_MEDIA_CONTROLLER

dev->mdev.dev = &pdev->dev;
strscpy(dev->mdev.model, "vim2m", sizeof(dev->mdev.model));
media_device_init(&dev->mdev);
dev->mdev.ops = &m2m_media_ops;
dev->v4l2_dev.mdev = &dev->mdev;

ret = v4l2_m2m_register_media_controller(dev->m2m_dev,
		vfd, MEDIA_ENT_F_PROC_VIDEO_SCALER);
if (ret) {
	v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n");
	goto unreg_m2m;
}

ret = media_device_register(&dev->mdev);
if (ret) {
	v4l2_err(&dev->v4l2_dev, "Failed to register mem2mem media device\n");
	goto unreg_m2m_mc;
}

endif

return 0;

ifdef CONFIG_MEDIA_CONTROLLER

unreg_m2m_mc:
v4l2_m2m_unregister_media_controller(dev->m2m_dev);
unreg_m2m:
v4l2_m2m_release(dev->m2m_dev);

endif

unreg_dev:
video_unregister_device(&dev->vfd);
unreg_v4l2:
v4l2_device_unregister(&dev->v4l2_dev);

return ret;

}

static int vim2m_remove(struct platform_device *pdev)
{
struct vim2m_dev *dev = platform_get_drvdata(pdev);

v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_NAME);

ifdef CONFIG_MEDIA_CONTROLLER

media_device_unregister(&dev->mdev);
v4l2_m2m_unregister_media_controller(dev->m2m_dev);
media_device_cleanup(&dev->mdev);

endif

v4l2_m2m_release(dev->m2m_dev);
video_unregister_device(&dev->vfd);
v4l2_device_unregister(&dev->v4l2_dev);

return 0;

}

/* Platform driver */

static const struct of_device_id vim2m_match[] = {
{ .compatible = "vim2m_match", },
{},
};
MODULE_DEVICE_TABLE(of, vim2m_match);

static struct platform_driver vim2m_pdrv = {
.probe = vim2m_probe,
.remove = vim2m_remove,
.driver = {
.name = MEM2MEM_NAME,
.of_match_table = vim2m_match,
},
};

static void __exit vim2m_exit(void)
{
platform_driver_unregister(&vim2m_pdrv);
// platform_device_unregister(&vim2m_pdev);
}

static int __init vim2m_init(void)
{
int ret;

// ret = platform_device_register(&vim2m_pdev);
// if (ret)
// 	return ret;

ret = platform_driver_register(&vim2m_pdrv);
if (ret)
	platform_device_unregister(&vim2m_pdev);

return ret;

}

module_init(vim2m_init);
module_exit(vim2m_exit);

/*

  • ionapp_export.c
  • It is a user space utility to create and export android
  • ion memory buffer fd to another process using unix domain socket as IPC.
  • This acts like a server for ionapp_import(client).
  • So, this server has to be started first before the client.
  • Copyright (C) 2017 Pintu Kumar pintu.ping@gmail.com
  • This software is licensed under the terms of the GNU General Public
  • License version 2, as published by the Free Software Foundation, and
  • may be copied, distributed, and modified under those terms.
  • 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 <stdio.h>

include <stdlib.h>

include <string.h>

include <unistd.h>

include <errno.h>

include <sys/time.h>

include "ionutils.h"

include "ipcsocket.h"

include <stdio.h>

include <stdlib.h>

include <stdint.h>

include <unistd.h>

include <fcntl.h>

include <string.h>

include <errno.h>

include <sys/ioctl.h>

include <sys/mman.h>

include <time.h>

include <signal.h>

include <poll.h>

include <linux/videodev2.h>

include <linux/v4l2-controls.h>

include <linux/media.h>

struct v4l2_buf_user {
int length;
void *mm_addr;
};

struct v4l2_buf_user output_user;
struct v4l2_buf_user capture_user;

static void open_device(const char *vim2m, int *fd)
{
// struct media_request_new new;
// int ret;

printf("Opening %s\n", vim2m);
*fd = open(vim2m, O_RDWR, 0);
if (*fd < 0) {
	perror("cannot open media file");
	exit(EXIT_FAILURE);
}

// memset(&new, 0, sizeof(new));
// new.flags |= MEDIA_REQUEST_FLAG_TEST;
// ret = ioctl(*fd, VIDIOC_NEW_REQUEST, &new);
// if (ret < 0) {
// 	printf("Requests not supported by this device! Aborting...\n");
// 	exit(EXIT_FAILURE);
// }

}

static void set_format(int fd, int buf_type)
{
struct v4l2_format format;
int ret;

memset(&format, 0, sizeof(format));
format.type = buf_type;
format.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
format.fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
format.fmt.pix.width = 1280;
format.fmt.pix.height = 720;
ret = ioctl(fd, VIDIOC_S_FMT, &format);
if (ret < 0) {
	perror("error while setting format");
	exit(EXIT_FAILURE);
}

}

static void request_buffers(int fd, int buf_type, unsigned int count)
{
struct v4l2_requestbuffers requestbuffers;
int ret;

memset(&requestbuffers, 0, sizeof(requestbuffers));
requestbuffers.type = buf_type;
requestbuffers.memory = V4L2_MEMORY_DMABUF;
requestbuffers.count = count;
ret = ioctl(fd, VIDIOC_REQBUFS, &requestbuffers);
if (ret < 0) {
	perror("error while requesting buffers");
	exit(EXIT_FAILURE);
}

if (count > 0)
	printf("%d %s buffers allocated\n", requestbuffers.count,
	       buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE ?
	       "capture" : "output");
else
	printf("All %s buffers freed\n",
	       buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE ?
	       "capture" : "output");

}

static void query_buf(int fd, int buf_type, int index, struct v4l2_buffer *buf)
{
int ret;

memset(buf, 0, sizeof(*buf));
buf->memory = V4L2_MEMORY_MMAP;
buf->type = buf_type;
buf->index = index;

ret = ioctl(fd, VIDIOC_QUERYBUF, buf);
if (ret < 0) {
	perror("error while querying buffer");
	exit(EXIT_FAILURE);
}

}

static void mmap_user_buf(int fd, struct v4l2_buffer *vb, struct v4l2_buf_user *vbu)
{
vbu->length = vb->length;
vbu->mm_addr = mmap(NULL, vbu->length, (PROT_READ | PROT_WRITE),
MAP_SHARED, fd, 0);

if (vbu->mm_addr == MAP_FAILED) {
	printf("libv4lcontrol: error shm mmap failed\n");
} else {
    printf("map success\n");
}

}

static void unmap_user_buf(struct v4l2_buf_user *vbu)
{
if (-1 == munmap (vbu->mm_addr, vbu->length)) {
printf ("munmap error\n");
} else {
printf("munmap sucess");
}
}

static void alloc_request(int fd, int *req)
{
// struct media_request_new new;
// int ret;

// memset(&new, 0, sizeof(new));
// ret = ioctl(fd, VIDIOC_NEW_REQUEST, &new);
// if (ret < 0) {
// 	perror("error while allocating request");
// 	exit(EXIT_FAILURE);
// }

// *req = new.fd;
*req = fd;

}

static void submit_request(int req)
{
// int ret;

// ret = ioctl(req, MEDIA_REQUEST_IOC_SUBMIT, NULL);
// if (ret < 0) {
// 	perror("error while queueing request");
// 	exit(EXIT_FAILURE);
// }

}

static void set_stream(int fd, int buf_type, int on)
{
int ret;

ret = ioctl(fd, on ? VIDIOC_STREAMON : VIDIOC_STREAMOFF, &buf_type);	/* 传入type */
if (ret < 0) {
	perror("error while setting streaming");
	exit(EXIT_FAILURE);
}
printf("%s stream %s\n", buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE ?
       "Capture" : "Output", on ? "activated" : "deactivated");

}

static void set_vim2m_ctrls(int fd, int req, unsigned int trans_time,
int hflip, int vflip)
{
struct v4l2_ext_control ctrl[3];
struct v4l2_ext_controls ctrls;
int ret;

memset(&ctrl, 0, sizeof(ctrl));
ctrl[0].id = (V4L2_CID_USER_BASE + 0x1000); // Transaction time
ctrl[0].value = trans_time;
ctrl[1].id = V4L2_CID_HFLIP;
ctrl[1].value = hflip;
ctrl[2].id = V4L2_CID_VFLIP;
ctrl[2].value = vflip;

memset(&ctrls, 0, sizeof(ctrls));
ctrls.request_fd = req;
ctrls.which = V4L2_CTRL_WHICH_CUR_VAL;
ctrls.count = 3;
ctrls.controls = ctrl;

ret = ioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls);
if (ret < 0) {
	perror("error while setting control value");
	exit(EXIT_FAILURE);
}

}

static void get_vim2m_ctrls(int fd, int req, int *trans_time,
int *hflip, int *vflip)
{
struct v4l2_ext_control ctrl[3];
struct v4l2_ext_controls ctrls;
int ret;

memset(&ctrl, 0, sizeof(ctrl));
ctrl[0].id = (V4L2_CID_USER_BASE + 0x1000); // Transaction time
ctrl[1].id = V4L2_CID_HFLIP;
ctrl[2].id = V4L2_CID_VFLIP;

memset(&ctrls, 0, sizeof(ctrls));
ctrls.request_fd = req;
ctrls.which = V4L2_CTRL_WHICH_CUR_VAL;
ctrls.count = 3;
ctrls.controls = ctrl;

ret = ioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls);
if (ret < 0) {
	perror("error while getting control value");
	exit(EXIT_FAILURE);
}

if (trans_time)
	*trans_time = ctrl[0].value;
if (hflip)
	*hflip = ctrl[1].value;
if (vflip)
	*vflip = ctrl[2].value;

}

static void qbuf(int fd, struct v4l2_buffer *buf, int dmabuf_fd)
{
int ret;

buf->m.fd = dmabuf_fd;
ret = ioctl(fd, VIDIOC_QBUF, buf);
if (ret < 0) {
	perror("error while queuing buffer");
	exit(EXIT_FAILURE);
}
printf("%s buffer %d queued, request FD %d\n",
       buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? "Capture" : "Output",
       buf->index, buf->request_fd);

}

static int dqbuf(int fd, int buf_type)
{
struct v4l2_buffer bufferinfo;
int ret;

memset(&bufferinfo, 0, sizeof bufferinfo);
bufferinfo.memory = V4L2_MEMORY_MMAP;
bufferinfo.type = buf_type;
ret = ioctl(fd, VIDIOC_DQBUF, &bufferinfo);
if (ret < 0) {
	perror("error while dequeuing buffer");
	exit(EXIT_FAILURE);
}
printf("%s buffer %d dequeued, request FD %d\n",
       buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? "Capture" : "Output",
       bufferinfo.index, bufferinfo.request_fd);

return bufferinfo.index;

}

int v4l2_main(int dmabuf_fd)
{
struct v4l2_buffer output_buf;
struct v4l2_buffer capture_buf;
int request;
int trans_time, hflp, vflp;
int fd;
// int i;
struct pollfd pfds = {
.events = POLLIN,
};

open_device("/dev/video3", &fd);

/* Set format on capture and output queues */
set_format(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE);
set_format(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT);

request_buffers(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, 1);
request_buffers(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT, 1);

query_buf(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0, &output_buf);
output_buf.bytesused = output_buf.length;
query_buf(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, &capture_buf);

// mmap_user_buf(fd, &output_buf, &output_user);
// mmap_user_buf(fd, &capture_buf, &capture_user);
// memcpy((char *)output_user.mm_addr, "liupf outptut tset", sizeof("liupf outptut tset"));
// memcpy((char *)capture_user.mm_addr, "liupf capture tset", sizeof("liupf capture tset"));

alloc_request(fd, &request);
printf("Allocated request %d\n", request);

set_stream(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT, 1);
set_stream(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, 1);

set_vim2m_ctrls(fd, request, 600, 1, 0);

/* Query the controls on the requests and the hardware */
get_vim2m_ctrls(fd, request, &trans_time, &hflp, &vflp);
printf("Pre-submit first request transaction time: %d, hflip: %d, vflip: %d\n",
       trans_time, hflp, vflp);
get_vim2m_ctrls(fd, 0, &trans_time, &hflp, &vflp);
printf("Pre-submit HW transaction time: %d, hflip: %d, vflip: %d\n",
       trans_time, hflp, vflp);

/* Queue buffers for our first request. Even though the stream is active
 * on both queues, streaming will not start until the request is queued */
output_buf.request_fd = request;
qbuf(fd, &output_buf, dmabuf_fd);
qbuf(fd, &capture_buf, dmabuf_fd);

/* Queue the request. This will apply the controls previously set and
 * actually start streaming */
submit_request(request);

/* Poll on the requests */
pfds.fd = request;

printf("Poll on one requests\n");
poll(&pfds, 1, -1);	
printf("%08x\n", pfds.revents);

/* Dequeue all buffers. */
dqbuf(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT);
dqbuf(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE);

set_stream(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0);
set_stream(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0);

/* Query the controls on the completed requests and the hardware */
get_vim2m_ctrls(fd, request, &trans_time, &hflp, &vflp);
printf("Post-dequeue first request transaction time: %d, hflip: %d, vflip: %d\n",
       trans_time, hflp, vflp);
get_vim2m_ctrls(fd, 0, &trans_time, &hflp, &vflp);
printf("Post-dequeue HW transaction time: %d, hflip: %d, vflip: %d\n",
       trans_time, hflp, vflp);

unmap_user_buf(&output_user);
unmap_user_buf(&capture_user);
/* free buffers */
request_buffers(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0);
request_buffers(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0);

/* Close all requests */
close(request);

close(fd);

return 0;

}

void print_usage(int argc, char *argv[])
{
printf("Usage: %s [-h ] [-i ] [-s ]\n",
argv[0]);
}

int main(int argc, char *argv[])
{
int opt, ret, status, heapid;
int sockfd, client_fd, shared_fd;
unsigned char *map_buf;
unsigned long map_len, heap_type, heap_size, flags;
struct ion_buffer_info info;
struct socket_info skinfo;

if (argc < 2) {
	print_usage(argc, argv);
	return -1;
}

heap_size = 0;
flags = 0;
heap_type = ION_HEAP_TYPE_DMA;

while ((opt = getopt(argc, argv, "hi:s:")) != -1) {
	switch (opt) {
	case 'h':
		print_usage(argc, argv);
		exit(0);
		break;
	case 'i':
		heapid = atoi(optarg);
		switch (heapid) {
		case 0:
			heap_type = ION_HEAP_TYPE_SYSTEM;
			break;
		case 1:
			heap_type = ION_HEAP_TYPE_SYSTEM_CONTIG;
			break;
		default:
			printf("ERROR: heap type not supported\n");
			exit(1);
		}
		break;
	case 's':
		heap_size = atoi(optarg);
		break;
	default:
		print_usage(argc, argv);
		exit(1);
		break;
	}
}

if (heap_size <= 0) {
	printf("heap_size cannot be 0\n");
	print_usage(argc, argv);
	exit(1);
}

printf("exporter: heap_type: %ld, heap_size: %ld\n", heap_type, heap_size);
info.heap_type = ION_HEAP_TYPE_DMA;
info.heap_size = heap_size;
info.heap_size = 777777;
info.flag_type = flags;
printf("%s %d\n", __func__, __LINE__);
/* This is server: open the socket connection first */
/* Here; 1 indicates server or exporter */
// status = opensocket(&sockfd, SOCKET_NAME, 1);
// if (status < 0) {
// 	fprintf(stderr, "<%s>: Failed opensocket.\n", __func__);
// 	goto err_socket;
// }
printf("%s %d\n", __func__, __LINE__);
skinfo.sockfd = sockfd;	/* 用于传递套接字的fd */
printf("%s %d\n", __func__, __LINE__);
ret = ion_export_buffer_fd(&info);
if (ret < 0) {
	fprintf(stderr, "FAILED: ion_get_buffer_fd\n");
	goto err_export;
}
client_fd = info.ionfd;
shared_fd = info.buffd;
map_buf = info.buffer;
map_len = info.buflen;
write_buffer_exporter(map_buf, map_len);	/* 写入数据 */

/* share ion buf fd with other user process */
printf("exporter: Sharing fd: %d, Client fd: %d\n", shared_fd, client_fd);
skinfo.datafd = shared_fd;	/* dmabuf的fd */
skinfo.buflen = map_len;
printf("%s %d\n", __func__, __LINE__);
// ret = socket_send_fd(&skinfo);
if (ret < 0) {
	fprintf(stderr, "FAILED: socket_send_fd\n");
	goto err_send;
}
printf("%s %d\n", __func__, __LINE__);
v4l2_main(shared_fd);

err_send:
err_export:
ion_close_buffer_fd(&info);

err_socket:
closesocket(sockfd, SOCKET_NAME);

return 0;

}

vim2m@a000000 {
	// dma-coherent;
	// memory-region = <&flash_memory>;
	// interrupts = <0x00 0x10 0x01>;
	// reg = <0x00 0xa000000 0x00 0x200>;
	compatible = "vim2m_match";
	iommus = <&iommu 0 0>;
};
posted @ 2025-03-26 23:26  _xingxing  阅读(29)  评论(0)    收藏  举报