Commit 57868acc authored by Satendra Singh Thakur's avatar Satendra Singh Thakur Committed by Mauro Carvalho Chehab

media: videobuf2: Add new uAPI for DVB streaming I/O

Adds a new uAPI for DVB to use streaming I/O which is implemented
based on videobuf2, using those new ioctls:

- DMX_REQBUFS:  Request kernel to allocate buffers which count and size
	        are dedicated by user.
- DMX_QUERYBUF: Get the buffer information like a memory offset which
		will mmap() and be shared with user-space.
- DMX_EXPBUF:   Just for testing whether buffer-exporting success or not.
- DMX_QBUF:     Pass the buffer to kernel-space.
- DMX_DQBUF:    Get back the buffer which may contain TS data.

Originally developed by: Junghak Sung <jh1009.sung@samsung.com>, as
seen at:
	https://patchwork.linuxtv.org/patch/31613/
	https://patchwork.kernel.org/patch/7334301/

The original patch was written before merging VB2-core functionalities
upstream. When such series was added, several adjustments were made,
fixing some issues with	V4L2, causing the original patch to be
non-trivially rebased.

After rebased, a few bugs in the patch were fixed. The patch was
also enhanced it and polling functionality got added.

The main changes over the original patch are:

dvb_vb2_fill_buffer():
	- Set the size of the outgoing buffer after while loop using
	  vb2_set_plane_payload;

	- Added NULL check for source buffer as per normal convention
	  of demux driver, this is called twice, first time with valid
	  buffer second time with NULL pointer, if its not handled,
	  it will result in  crash

	- Restricted spinlock for only list_* operations

dvb_vb2_init():
	- Restricted q->io_modes to only VB2_MMAP as its the only
	  supported mode

dvb_vb2_release():
	- Replaced the && in if condiion with &, because otherwise
	  it was always getting satisfied.

dvb_vb2_stream_off():
	- Added list_del code for enqueud buffers upon stream off

dvb_vb2_poll():
	- Added this new function in order to support polling

dvb_demux_poll() and dvb_dvr_poll()
	- dvb_vb2_poll() is now called from these functions

- Ported this patch and latest videobuf2 to lower kernel versions and
  tested auto scan.
Co-developed-by: default avatarJunghak Sung <jh1009.sung@samsung.com>

[mchehab@s-opensource.com: checkpatch fixes]
Signed-off-by: default avatarJunghak Sung <jh1009.sung@samsung.com>
Signed-off-by: default avatarGeunyoung Kim <nenggun.kim@samsung.com>
Acked-by: default avatarSeung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: default avatarInki Dae <inki.dae@samsung.com>
Signed-off-by: default avatarSatendra Singh Thakur <satendra.t@samsung.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent 9eb124fe
......@@ -7,6 +7,6 @@ dvb-net-$(CONFIG_DVB_NET) := dvb_net.o
dvb-core-objs := dvbdev.o dmxdev.o dvb_demux.o \
dvb_ca_en50221.o dvb_frontend.o \
$(dvb-net-y) dvb_ringbuffer.o dvb_math.o
$(dvb-net-y) dvb_ringbuffer.o dvb_vb2.o dvb_math.o
obj-$(CONFIG_DVB_CORE) += dvb-core.o
......@@ -28,6 +28,7 @@
#include <linux/wait.h>
#include <linux/uaccess.h>
#include "dmxdev.h"
#include "dvb_vb2.h"
static int debug;
......@@ -138,14 +139,8 @@ static int dvb_dvr_open(struct inode *inode, struct file *file)
return -ENODEV;
}
if ((file->f_flags & O_ACCMODE) == O_RDWR) {
if (!(dmxdev->capabilities & DMXDEV_CAP_DUPLEX)) {
mutex_unlock(&dmxdev->mutex);
return -EOPNOTSUPP;
}
}
if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
if (((file->f_flags & O_ACCMODE) == O_RDONLY) ||
((file->f_flags & O_ACCMODE) == O_RDWR)) {
void *mem;
if (!dvbdev->readers) {
......@@ -158,6 +153,8 @@ static int dvb_dvr_open(struct inode *inode, struct file *file)
return -ENOMEM;
}
dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE);
dvb_vb2_init(&dmxdev->dvr_vb2_ctx, "dvr",
file->f_flags & O_NONBLOCK);
dvbdev->readers--;
}
......@@ -195,7 +192,11 @@ static int dvb_dvr_release(struct inode *inode, struct file *file)
dmxdev->demux->connect_frontend(dmxdev->demux,
dmxdev->dvr_orig_fe);
}
if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
if (((file->f_flags & O_ACCMODE) == O_RDONLY) ||
((file->f_flags & O_ACCMODE) == O_RDWR)) {
if (dvb_vb2_is_streaming(&dmxdev->dvr_vb2_ctx))
dvb_vb2_stream_off(&dmxdev->dvr_vb2_ctx);
dvb_vb2_release(&dmxdev->dvr_vb2_ctx);
dvbdev->readers++;
if (dmxdev->dvr_buffer.data) {
void *mem = dmxdev->dvr_buffer.data;
......@@ -358,8 +359,8 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
{
struct dmxdev_filter *dmxdevfilter = filter->priv;
int ret;
if (dmxdevfilter->buffer.error) {
if (!dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx) &&
dmxdevfilter->buffer.error) {
wake_up(&dmxdevfilter->buffer.queue);
return 0;
}
......@@ -370,11 +371,19 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
}
del_timer(&dmxdevfilter->timer);
dprintk("section callback %*ph\n", 6, buffer1);
ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer1,
buffer1_len);
if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx)) {
ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx,
buffer1, buffer1_len);
if (ret == buffer1_len)
ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx,
buffer2, buffer2_len);
} else {
ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer,
buffer1, buffer1_len);
if (ret == buffer1_len) {
ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer2,
buffer2_len);
ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer,
buffer2, buffer2_len);
}
}
if (ret < 0)
dmxdevfilter->buffer.error = ret;
......@@ -391,6 +400,7 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
{
struct dmxdev_filter *dmxdevfilter = feed->priv;
struct dvb_ringbuffer *buffer;
struct dvb_vb2_ctx *ctx;
int ret;
spin_lock(&dmxdevfilter->dev->lock);
......@@ -399,11 +409,20 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
return 0;
}
if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
|| dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)
if (dmxdevfilter->params.pes.output == DMX_OUT_TAP ||
dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP) {
buffer = &dmxdevfilter->buffer;
else
ctx = &dmxdevfilter->vb2_ctx;
} else {
buffer = &dmxdevfilter->dev->dvr_buffer;
ctx = &dmxdevfilter->dev->dvr_vb2_ctx;
}
if (dvb_vb2_is_streaming(ctx)) {
ret = dvb_vb2_fill_buffer(ctx, buffer1, buffer1_len);
if (ret == buffer1_len)
ret = dvb_vb2_fill_buffer(ctx, buffer2, buffer2_len);
} else {
if (buffer->error) {
spin_unlock(&dmxdevfilter->dev->lock);
wake_up(&buffer->queue);
......@@ -411,7 +430,9 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
}
ret = dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len);
if (ret == buffer1_len)
ret = dvb_dmxdev_buffer_write(buffer, buffer2, buffer2_len);
ret = dvb_dmxdev_buffer_write(buffer,
buffer2, buffer2_len);
}
if (ret < 0)
buffer->error = ret;
spin_unlock(&dmxdevfilter->dev->lock);
......@@ -750,6 +771,8 @@ static int dvb_demux_open(struct inode *inode, struct file *file)
file->private_data = dmxdevfilter;
dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192);
dvb_vb2_init(&dmxdevfilter->vb2_ctx, "demux_filter",
file->f_flags & O_NONBLOCK);
dmxdevfilter->type = DMXDEV_TYPE_NONE;
dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
timer_setup(&dmxdevfilter->timer, dvb_dmxdev_filter_timeout, 0);
......@@ -765,6 +788,10 @@ static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev,
{
mutex_lock(&dmxdev->mutex);
mutex_lock(&dmxdevfilter->mutex);
if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx))
dvb_vb2_stream_off(&dmxdevfilter->vb2_ctx);
dvb_vb2_release(&dmxdevfilter->vb2_ctx);
dvb_dmxdev_filter_stop(dmxdevfilter);
dvb_dmxdev_filter_reset(dmxdevfilter);
......@@ -1052,6 +1079,53 @@ static int dvb_demux_do_ioctl(struct file *file,
mutex_unlock(&dmxdevfilter->mutex);
break;
case DMX_REQBUFS:
if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
mutex_unlock(&dmxdev->mutex);
return -ERESTARTSYS;
}
ret = dvb_vb2_reqbufs(&dmxdevfilter->vb2_ctx, parg);
mutex_unlock(&dmxdevfilter->mutex);
break;
case DMX_QUERYBUF:
if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
mutex_unlock(&dmxdev->mutex);
return -ERESTARTSYS;
}
ret = dvb_vb2_querybuf(&dmxdevfilter->vb2_ctx, parg);
mutex_unlock(&dmxdevfilter->mutex);
break;
case DMX_EXPBUF:
if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
mutex_unlock(&dmxdev->mutex);
return -ERESTARTSYS;
}
ret = dvb_vb2_expbuf(&dmxdevfilter->vb2_ctx, parg);
mutex_unlock(&dmxdevfilter->mutex);
break;
case DMX_QBUF:
if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
mutex_unlock(&dmxdev->mutex);
return -ERESTARTSYS;
}
ret = dvb_vb2_qbuf(&dmxdevfilter->vb2_ctx, parg);
if (ret == 0 && !dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx))
ret = dvb_vb2_stream_on(&dmxdevfilter->vb2_ctx);
mutex_unlock(&dmxdevfilter->mutex);
break;
case DMX_DQBUF:
if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
mutex_unlock(&dmxdev->mutex);
return -ERESTARTSYS;
}
ret = dvb_vb2_dqbuf(&dmxdevfilter->vb2_ctx, parg);
mutex_unlock(&dmxdevfilter->mutex);
break;
default:
ret = -EINVAL;
break;
......@@ -1073,6 +1147,8 @@ static unsigned int dvb_demux_poll(struct file *file, poll_table *wait)
if ((!dmxdevfilter) || dmxdevfilter->dev->exit)
return POLLERR;
if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx))
return dvb_vb2_poll(&dmxdevfilter->vb2_ctx, file, wait);
poll_wait(file, &dmxdevfilter->buffer.queue, wait);
......@@ -1090,11 +1166,31 @@ static unsigned int dvb_demux_poll(struct file *file, poll_table *wait)
return mask;
}
static int dvb_demux_release(struct inode *inode, struct file *file)
static int dvb_demux_mmap(struct file *file, struct vm_area_struct *vma)
{
struct dmxdev_filter *dmxdevfilter = file->private_data;
struct dmxdev *dmxdev = dmxdevfilter->dev;
int ret;
if (mutex_lock_interruptible(&dmxdev->mutex))
return -ERESTARTSYS;
if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
mutex_unlock(&dmxdev->mutex);
return -ERESTARTSYS;
}
ret = dvb_vb2_mmap(&dmxdevfilter->vb2_ctx, vma);
mutex_unlock(&dmxdevfilter->mutex);
mutex_unlock(&dmxdev->mutex);
return ret;
}
static int dvb_demux_release(struct inode *inode, struct file *file)
{
struct dmxdev_filter *dmxdevfilter = file->private_data;
struct dmxdev *dmxdev = dmxdevfilter->dev;
int ret;
ret = dvb_dmxdev_filter_free(dmxdev, dmxdevfilter);
......@@ -1118,6 +1214,7 @@ static const struct file_operations dvb_demux_fops = {
.release = dvb_demux_release,
.poll = dvb_demux_poll,
.llseek = default_llseek,
.mmap = dvb_demux_mmap,
};
static const struct dvb_device dvbdev_demux = {
......@@ -1146,6 +1243,28 @@ static int dvb_dvr_do_ioctl(struct file *file,
ret = dvb_dvr_set_buffer_size(dmxdev, arg);
break;
case DMX_REQBUFS:
ret = dvb_vb2_reqbufs(&dmxdev->dvr_vb2_ctx, parg);
break;
case DMX_QUERYBUF:
ret = dvb_vb2_querybuf(&dmxdev->dvr_vb2_ctx, parg);
break;
case DMX_EXPBUF:
ret = dvb_vb2_expbuf(&dmxdev->dvr_vb2_ctx, parg);
break;
case DMX_QBUF:
ret = dvb_vb2_qbuf(&dmxdev->dvr_vb2_ctx, parg);
if (ret == 0 && !dvb_vb2_is_streaming(&dmxdev->dvr_vb2_ctx))
ret = dvb_vb2_stream_on(&dmxdev->dvr_vb2_ctx);
break;
case DMX_DQBUF:
ret = dvb_vb2_dqbuf(&dmxdev->dvr_vb2_ctx, parg);
break;
default:
ret = -EINVAL;
break;
......@@ -1170,10 +1289,13 @@ static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait)
if (dmxdev->exit)
return POLLERR;
if (dvb_vb2_is_streaming(&dmxdev->dvr_vb2_ctx))
return dvb_vb2_poll(&dmxdev->dvr_vb2_ctx, file, wait);
poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
if (((file->f_flags & O_ACCMODE) == O_RDONLY) ||
((file->f_flags & O_ACCMODE) == O_RDWR)) {
if (dmxdev->dvr_buffer.error)
mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
......@@ -1185,6 +1307,23 @@ static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait)
return mask;
}
static int dvb_dvr_mmap(struct file *file, struct vm_area_struct *vma)
{
struct dvb_device *dvbdev = file->private_data;
struct dmxdev *dmxdev = dvbdev->priv;
int ret;
if (dmxdev->exit)
return -ENODEV;
if (mutex_lock_interruptible(&dmxdev->mutex))
return -ERESTARTSYS;
ret = dvb_vb2_mmap(&dmxdev->dvr_vb2_ctx, vma);
mutex_unlock(&dmxdev->mutex);
return ret;
}
static const struct file_operations dvb_dvr_fops = {
.owner = THIS_MODULE,
.read = dvb_dvr_read,
......@@ -1194,6 +1333,7 @@ static const struct file_operations dvb_dvr_fops = {
.release = dvb_dvr_release,
.poll = dvb_dvr_poll,
.llseek = default_llseek,
.mmap = dvb_dvr_mmap,
};
static const struct dvb_device dvbdev_dvr = {
......
......@@ -35,6 +35,7 @@
#include "dvbdev.h"
#include "demux.h"
#include "dvb_ringbuffer.h"
#include "dvb_vb2.h"
/**
* enum dmxdev_type - type of demux filter type.
......@@ -140,6 +141,7 @@ struct dmxdev_filter {
enum dmxdev_state state;
struct dmxdev *dev;
struct dvb_ringbuffer buffer;
struct dvb_vb2_ctx vb2_ctx;
struct mutex mutex;
......@@ -183,6 +185,8 @@ struct dmxdev {
struct dvb_ringbuffer dvr_buffer;
#define DVR_BUFFER_SIZE (10*188*1024)
struct dvb_vb2_ctx dvr_vb2_ctx;
struct mutex mutex;
spinlock_t lock;
};
......
This diff is collapsed.
/*
* dvb-vb2.h - DVB driver helper framework for streaming I/O
*
* Copyright (C) 2015 Samsung Electronics
*
* Author: jh1009.sung@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.
*/
#ifndef _DVB_VB2_H
#define _DVB_VB2_H
#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/dvb/dmx.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-dma-contig.h>
#include <media/videobuf2-vmalloc.h>
enum dvb_buf_type {
DVB_BUF_TYPE_CAPTURE = 1,
DVB_BUF_TYPE_OUTPUT = 2,
};
#define DVB_VB2_STATE_NONE (0x0)
#define DVB_VB2_STATE_INIT (0x1)
#define DVB_VB2_STATE_REQBUFS (0x2)
#define DVB_VB2_STATE_STREAMON (0x4)
#define DVB_VB2_NAME_MAX (20)
struct dvb_buffer {
struct vb2_buffer vb;
struct list_head list;
};
struct dvb_vb2_ctx {
struct vb2_queue vb_q;
struct mutex mutex;
spinlock_t slock;
struct list_head dvb_q;
struct dvb_buffer *buf;
int offset;
int remain;
int state;
int buf_siz;
int buf_cnt;
int nonblocking;
char name[DVB_VB2_NAME_MAX + 1];
};
int dvb_vb2_init(struct dvb_vb2_ctx *ctx, const char *name, int non_blocking);
int dvb_vb2_release(struct dvb_vb2_ctx *ctx);
int dvb_vb2_stream_on(struct dvb_vb2_ctx *ctx);
int dvb_vb2_stream_off(struct dvb_vb2_ctx *ctx);
int dvb_vb2_is_streaming(struct dvb_vb2_ctx *ctx);
int dvb_vb2_fill_buffer(struct dvb_vb2_ctx *ctx,
const unsigned char *src, int len);
int dvb_vb2_reqbufs(struct dvb_vb2_ctx *ctx, struct dmx_requestbuffers *req);
int dvb_vb2_querybuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b);
int dvb_vb2_expbuf(struct dvb_vb2_ctx *ctx, struct dmx_exportbuffer *exp);
int dvb_vb2_qbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b);
int dvb_vb2_dqbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b);
int dvb_vb2_mmap(struct dvb_vb2_ctx *ctx, struct vm_area_struct *vma);
unsigned int dvb_vb2_poll(struct dvb_vb2_ctx *ctx, struct file *file,
poll_table *wait);
#endif /* _DVB_VB2_H */
......@@ -211,6 +211,64 @@ struct dmx_stc {
__u64 stc;
};
/**
* struct dmx_buffer - dmx buffer info
*
* @index: id number of the buffer
* @bytesused: number of bytes occupied by data in the buffer (payload);
* @offset: for buffers with memory == DMX_MEMORY_MMAP;
* offset from the start of the device memory for this plane,
* (or a "cookie" that should be passed to mmap() as offset)
* @length: size in bytes of the buffer
*
* Contains data exchanged by application and driver using one of the streaming
* I/O methods.
*/
struct dmx_buffer {
__u32 index;
__u32 bytesused;
__u32 offset;
__u32 length;
__u32 reserved[4];
};
/**
* struct dmx_requestbuffers - request dmx buffer information
*
* @count: number of requested buffers,
* @size: size in bytes of the requested buffer
*
* Contains data used for requesting a dmx buffer.
* All reserved fields must be set to zero.
*/
struct dmx_requestbuffers {
__u32 count;
__u32 size;
__u32 reserved[2];
};
/**
* struct dmx_exportbuffer - export of dmx buffer as DMABUF file descriptor
*
* @index: id number of the buffer
* @flags: flags for newly created file, currently only O_CLOEXEC is
* supported, refer to manual of open syscall for more details
* @fd: file descriptor associated with DMABUF (set by driver)
*
* Contains data used for exporting a dmx buffer as DMABUF file descriptor.
* The buffer is identified by a 'cookie' returned by DMX_QUERYBUF
* (identical to the cookie used to mmap() the buffer to userspace). All
* reserved fields must be set to zero. The field reserved0 is expected to
* become a structure 'type' allowing an alternative layout of the structure
* content. Therefore this field should not be used for any other extensions.
*/
struct dmx_exportbuffer {
__u32 index;
__u32 flags;
__s32 fd;
__u32 reserved[5];
};
#define DMX_START _IO('o', 41)
#define DMX_STOP _IO('o', 42)
#define DMX_SET_FILTER _IOW('o', 43, struct dmx_sct_filter_params)
......@@ -231,4 +289,10 @@ typedef struct dmx_filter dmx_filter_t;
#endif
#endif /* _UAPI_DVBDMX_H_ */
#define DMX_REQBUFS _IOWR('o', 60, struct dmx_requestbuffers)
#define DMX_QUERYBUF _IOWR('o', 61, struct dmx_buffer)
#define DMX_EXPBUF _IOWR('o', 62, struct dmx_exportbuffer)
#define DMX_QBUF _IOWR('o', 63, struct dmx_buffer)
#define DMX_DQBUF _IOWR('o', 64, struct dmx_buffer)
#endif /* _DVBDMX_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment