summaryrefslogtreecommitdiff
path: root/drivers/media/dvb-core/dmxdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/dvb-core/dmxdev.c')
-rw-r--r--drivers/media/dvb-core/dmxdev.c430
1 files changed, 318 insertions, 112 deletions
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
index 0b4616b87195..8c6f5aafda1d 100644
--- a/drivers/media/dvb-core/dmxdev.c
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -1,25 +1,13 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
/*
* dmxdev.c - DVB demultiplexer device
*
* Copyright (C) 2000 Ralph Metzler & Marcus Metzler
* for convergence integrated media GmbH
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
*/
+#define pr_fmt(fmt) "dmxdev: " fmt
+
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
@@ -28,15 +16,20 @@
#include <linux/poll.h>
#include <linux/ioctl.h>
#include <linux/wait.h>
-#include <asm/uaccess.h>
-#include "dmxdev.h"
+#include <linux/uaccess.h>
+#include <media/dmxdev.h>
+#include <media/dvb_vb2.h>
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-#define dprintk if (debug) printk
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), \
+ __func__, ##arg); \
+} while (0)
static int dvb_dmxdev_buffer_write(struct dvb_ringbuffer *buf,
const u8 *src, size_t len)
@@ -50,7 +43,7 @@ static int dvb_dmxdev_buffer_write(struct dvb_ringbuffer *buf,
free = dvb_ringbuffer_free(buf);
if (len > free) {
- dprintk("dmxdev: buffer overflow\n");
+ dprintk("buffer overflow\n");
return -EOVERFLOW;
}
@@ -125,8 +118,9 @@ static int dvb_dvr_open(struct inode *inode, struct file *file)
struct dvb_device *dvbdev = file->private_data;
struct dmxdev *dmxdev = dvbdev->priv;
struct dmx_frontend *front;
+ bool need_ringbuffer = false;
- dprintk("function : %s\n", __func__);
+ dprintk("%s\n", __func__);
if (mutex_lock_interruptible(&dmxdev->mutex))
return -ERESTARTSYS;
@@ -136,15 +130,35 @@ static int dvb_dvr_open(struct inode *inode, struct file *file)
return -ENODEV;
}
- if ((file->f_flags & O_ACCMODE) == O_RDWR) {
+ dmxdev->may_do_mmap = 0;
+
+ /*
+ * The logic here is a little tricky due to the ifdef.
+ *
+ * The ringbuffer is used for both read and mmap.
+ *
+ * It is not needed, however, on two situations:
+ * - Write devices (access with O_WRONLY);
+ * - For duplex device nodes, opened with O_RDWR.
+ */
+
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+ need_ringbuffer = true;
+ else if ((file->f_flags & O_ACCMODE) == O_RDWR) {
if (!(dmxdev->capabilities & DMXDEV_CAP_DUPLEX)) {
+#ifdef CONFIG_DVB_MMAP
+ dmxdev->may_do_mmap = 1;
+ need_ringbuffer = true;
+#else
mutex_unlock(&dmxdev->mutex);
return -EOPNOTSUPP;
+#endif
}
}
- if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+ if (need_ringbuffer) {
void *mem;
+
if (!dvbdev->readers) {
mutex_unlock(&dmxdev->mutex);
return -EBUSY;
@@ -155,6 +169,9 @@ static int dvb_dvr_open(struct inode *inode, struct file *file)
return -ENOMEM;
}
dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE);
+ if (dmxdev->may_do_mmap)
+ dvb_vb2_init(&dmxdev->dvr_vb2_ctx, "dvr",
+ file->f_flags & O_NONBLOCK);
dvbdev->readers--;
}
@@ -192,10 +209,18 @@ 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) ||
+ dmxdev->may_do_mmap) {
+ if (dmxdev->may_do_mmap) {
+ 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;
+ /*memory barrier*/
mb();
spin_lock_irq(&dmxdev->lock);
dmxdev->dvr_buffer.data = NULL;
@@ -206,8 +231,6 @@ static int dvb_dvr_release(struct inode *inode, struct file *file)
/* TODO */
dvbdev->users--;
if (dvbdev->users == 1 && dmxdev->exit == 1) {
- fops_put(file->f_op);
- file->f_op = NULL;
mutex_unlock(&dmxdev->mutex);
wake_up(&dvbdev->wait_queue);
} else
@@ -260,7 +283,7 @@ static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,
void *newmem;
void *oldmem;
- dprintk("function : %s\n", __func__);
+ dprintk("%s\n", __func__);
if (buf->size == size)
return 0;
@@ -327,9 +350,10 @@ static int dvb_dmxdev_set_buffer_size(struct dmxdev_filter *dmxdevfilter,
return 0;
}
-static void dvb_dmxdev_filter_timeout(unsigned long data)
+static void dvb_dmxdev_filter_timeout(struct timer_list *t)
{
- struct dmxdev_filter *dmxdevfilter = (struct dmxdev_filter *)data;
+ struct dmxdev_filter *dmxdevfilter = timer_container_of(dmxdevfilter,
+ t, timer);
dmxdevfilter->buffer.error = -ETIMEDOUT;
spin_lock_irq(&dmxdevfilter->dev->lock);
@@ -342,10 +366,8 @@ static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter)
{
struct dmx_sct_filter_params *para = &dmxdevfilter->params.sec;
- del_timer(&dmxdevfilter->timer);
+ timer_delete(&dmxdevfilter->timer);
if (para->timeout) {
- dmxdevfilter->timer.function = dvb_dmxdev_filter_timeout;
- dmxdevfilter->timer.data = (unsigned long)dmxdevfilter;
dmxdevfilter->timer.expires =
jiffies + 1 + (HZ / 2 + HZ * para->timeout) / 1000;
add_timer(&dmxdevfilter->timer);
@@ -355,12 +377,13 @@ static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter)
static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
const u8 *buffer2, size_t buffer2_len,
struct dmx_section_filter *filter,
- enum dmx_success success)
+ u32 *buffer_flags)
{
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;
}
@@ -369,13 +392,23 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
spin_unlock(&dmxdevfilter->dev->lock);
return 0;
}
- del_timer(&dmxdevfilter->timer);
- dprintk("dmxdev: section callback %*ph\n", 6, buffer1);
- ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer1,
- buffer1_len);
- if (ret == buffer1_len) {
- ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer2,
- buffer2_len);
+ timer_delete(&dmxdevfilter->timer);
+ dprintk("section callback %*ph\n", 6, buffer1);
+ if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx)) {
+ ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx,
+ buffer1, buffer1_len,
+ buffer_flags);
+ if (ret == buffer1_len)
+ ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx,
+ buffer2, buffer2_len,
+ buffer_flags);
+ } 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);
+ }
}
if (ret < 0)
dmxdevfilter->buffer.error = ret;
@@ -389,10 +422,13 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
const u8 *buffer2, size_t buffer2_len,
struct dmx_ts_feed *feed,
- enum dmx_success success)
+ u32 *buffer_flags)
{
struct dmxdev_filter *dmxdevfilter = feed->priv;
struct dvb_ringbuffer *buffer;
+#ifdef CONFIG_DVB_MMAP
+ struct dvb_vb2_ctx *ctx;
+#endif
int ret;
spin_lock(&dmxdevfilter->dev->lock);
@@ -401,19 +437,36 @@ 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
+#ifdef CONFIG_DVB_MMAP
+ ctx = &dmxdevfilter->vb2_ctx;
+#endif
+ } else {
buffer = &dmxdevfilter->dev->dvr_buffer;
- if (buffer->error) {
- spin_unlock(&dmxdevfilter->dev->lock);
- wake_up(&buffer->queue);
- return 0;
+#ifdef CONFIG_DVB_MMAP
+ ctx = &dmxdevfilter->dev->dvr_vb2_ctx;
+#endif
+ }
+
+ if (dvb_vb2_is_streaming(ctx)) {
+ ret = dvb_vb2_fill_buffer(ctx, buffer1, buffer1_len,
+ buffer_flags);
+ if (ret == buffer1_len)
+ ret = dvb_vb2_fill_buffer(ctx, buffer2, buffer2_len,
+ buffer_flags);
+ } else {
+ if (buffer->error) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up(&buffer->queue);
+ return 0;
+ }
+ 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, buffer1, buffer1_len);
- if (ret == buffer1_len)
- ret = dvb_dmxdev_buffer_write(buffer, buffer2, buffer2_len);
if (ret < 0)
buffer->error = ret;
spin_unlock(&dmxdevfilter->dev->lock);
@@ -430,7 +483,7 @@ static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter)
switch (dmxdevfilter->type) {
case DMXDEV_TYPE_SEC:
- del_timer(&dmxdevfilter->timer);
+ timer_delete(&dmxdevfilter->timer);
dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec);
break;
case DMXDEV_TYPE_PES:
@@ -560,9 +613,9 @@ static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev,
struct dmxdev_filter *filter,
struct dmxdev_feed *feed)
{
- struct timespec timeout = { 0 };
+ ktime_t timeout = ktime_set(0, 0);
struct dmx_pes_filter_params *para = &filter->params.pes;
- dmx_output_t otype;
+ enum dmx_output otype;
int ret;
int ts_type;
enum dmx_ts_pes ts_pes;
@@ -593,7 +646,7 @@ static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev,
tsfeed = feed->ts;
tsfeed->priv = filter;
- ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, 32768, timeout);
+ ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, timeout);
if (ret < 0) {
dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
return ret;
@@ -658,16 +711,16 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
ret = dmxdev->demux->allocate_section_feed(dmxdev->demux,
secfeed,
dvb_dmxdev_section_callback);
- if (ret < 0) {
- printk("DVB (%s): could not alloc feed\n",
+ if (!*secfeed) {
+ pr_err("DVB (%s): could not alloc feed\n",
__func__);
return ret;
}
- ret = (*secfeed)->set(*secfeed, para->pid, 32768,
+ ret = (*secfeed)->set(*secfeed, para->pid,
(para->flags & DMX_CHECK_CRC) ? 1 : 0);
if (ret < 0) {
- printk("DVB (%s): could not set feed\n",
+ pr_err("DVB (%s): could not set feed\n",
__func__);
dvb_dmxdev_feed_restart(filter);
return ret;
@@ -679,7 +732,7 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
ret = (*secfeed)->allocate_filter(*secfeed, secfilter);
if (ret < 0) {
dvb_dmxdev_feed_restart(filter);
- filter->feed.sec->start_filtering(*secfeed);
+ *secfeed = NULL;
dprintk("could not get filter\n");
return ret;
}
@@ -738,6 +791,11 @@ static int dvb_demux_open(struct inode *inode, struct file *file)
if (mutex_lock_interruptible(&dmxdev->mutex))
return -ERESTARTSYS;
+ if (dmxdev->exit) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENODEV;
+ }
+
for (i = 0; i < dmxdev->filternum; i++)
if (dmxdev->filter[i].state == DMXDEV_STATE_FREE)
break;
@@ -751,10 +809,18 @@ static int dvb_demux_open(struct inode *inode, struct file *file)
mutex_init(&dmxdevfilter->mutex);
file->private_data = dmxdevfilter;
+#ifdef CONFIG_DVB_MMAP
+ dmxdev->may_do_mmap = 1;
+#else
+ dmxdev->may_do_mmap = 0;
+#endif
+
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);
- init_timer(&dmxdevfilter->timer);
+ timer_setup(&dmxdevfilter->timer, dvb_dmxdev_filter_timeout, 0);
dvbdev->users++;
@@ -767,6 +833,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);
@@ -787,7 +857,7 @@ static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev,
return 0;
}
-static inline void invert_mode(dmx_filter_t *filter)
+static inline void invert_mode(struct dmx_filter *filter)
{
int i;
@@ -848,7 +918,7 @@ static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev,
struct dmxdev_filter *dmxdevfilter,
struct dmx_sct_filter_params *params)
{
- dprintk("function : %s, PID=0x%04x, flags=%02x, timeout=%d\n",
+ dprintk("%s: PID=0x%04x, flags=%02x, timeout=%d\n",
__func__, params->pid, params->flags, params->timeout);
dvb_dmxdev_filter_stop(dmxdevfilter);
@@ -874,7 +944,7 @@ static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
dvb_dmxdev_filter_stop(dmxdevfilter);
dvb_dmxdev_filter_reset(dmxdevfilter);
- if ((unsigned)params->pes_type > DMX_PES_OTHER)
+ if ((unsigned int)params->pes_type > DMX_PES_OTHER)
return -EINVAL;
dmxdevfilter->type = DMXDEV_TYPE_PES;
@@ -1025,22 +1095,6 @@ static int dvb_demux_do_ioctl(struct file *file,
dmxdev->demux->get_pes_pids(dmxdev->demux, parg);
break;
- case DMX_GET_CAPS:
- if (!dmxdev->demux->get_caps) {
- ret = -EINVAL;
- break;
- }
- ret = dmxdev->demux->get_caps(dmxdev->demux, parg);
- break;
-
- case DMX_SET_SOURCE:
- if (!dmxdev->demux->set_source) {
- ret = -EINVAL;
- break;
- }
- ret = dmxdev->demux->set_source(dmxdev->demux, parg);
- break;
-
case DMX_GET_STC:
if (!dmxdev->demux->get_stc) {
ret = -EINVAL;
@@ -1070,8 +1124,56 @@ static int dvb_demux_do_ioctl(struct file *file,
mutex_unlock(&dmxdevfilter->mutex);
break;
+#ifdef CONFIG_DVB_MMAP
+ 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;
+#endif
default:
- ret = -EINVAL;
+ ret = -ENOTTY;
break;
}
mutex_unlock(&dmxdev->mutex);
@@ -1084,44 +1186,69 @@ static long dvb_demux_ioctl(struct file *file, unsigned int cmd,
return dvb_usercopy(file, cmd, arg, dvb_demux_do_ioctl);
}
-static unsigned int dvb_demux_poll(struct file *file, poll_table *wait)
+static __poll_t dvb_demux_poll(struct file *file, poll_table *wait)
{
struct dmxdev_filter *dmxdevfilter = file->private_data;
- unsigned int mask = 0;
-
- if (!dmxdevfilter)
- return -EINVAL;
+ __poll_t mask = 0;
poll_wait(file, &dmxdevfilter->buffer.queue, wait);
+ if ((!dmxdevfilter) || dmxdevfilter->dev->exit)
+ return EPOLLERR;
+ if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx))
+ return dvb_vb2_poll(&dmxdevfilter->vb2_ctx, file, wait);
+
if (dmxdevfilter->state != DMXDEV_STATE_GO &&
dmxdevfilter->state != DMXDEV_STATE_DONE &&
dmxdevfilter->state != DMXDEV_STATE_TIMEDOUT)
return 0;
if (dmxdevfilter->buffer.error)
- mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+ mask |= (EPOLLIN | EPOLLRDNORM | EPOLLPRI | EPOLLERR);
if (!dvb_ringbuffer_empty(&dmxdevfilter->buffer))
- mask |= (POLLIN | POLLRDNORM | POLLPRI);
+ mask |= (EPOLLIN | EPOLLRDNORM | EPOLLPRI);
return mask;
}
-static int dvb_demux_release(struct inode *inode, struct file *file)
+#ifdef CONFIG_DVB_MMAP
+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 (!dmxdev->may_do_mmap)
+ return -ENOTTY;
+
+ 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;
+}
+#endif
+
+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);
mutex_lock(&dmxdev->mutex);
dmxdev->dvbdev->users--;
- if(dmxdev->dvbdev->users==1 && dmxdev->exit==1) {
- fops_put(file->f_op);
- file->f_op = NULL;
+ if (dmxdev->dvbdev->users == 1 && dmxdev->exit == 1) {
mutex_unlock(&dmxdev->mutex);
wake_up(&dmxdev->dvbdev->wait_queue);
} else
@@ -1134,16 +1261,23 @@ static const struct file_operations dvb_demux_fops = {
.owner = THIS_MODULE,
.read = dvb_demux_read,
.unlocked_ioctl = dvb_demux_ioctl,
+ .compat_ioctl = dvb_demux_ioctl,
.open = dvb_demux_open,
.release = dvb_demux_release,
.poll = dvb_demux_poll,
.llseek = default_llseek,
+#ifdef CONFIG_DVB_MMAP
+ .mmap = dvb_demux_mmap,
+#endif
};
-static struct dvb_device dvbdev_demux = {
+static const struct dvb_device dvbdev_demux = {
.priv = NULL,
.users = 1,
.writers = 1,
+#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
+ .name = "dvb-demux",
+#endif
.fops = &dvb_demux_fops
};
@@ -1163,8 +1297,31 @@ static int dvb_dvr_do_ioctl(struct file *file,
ret = dvb_dvr_set_buffer_size(dmxdev, arg);
break;
+#ifdef CONFIG_DVB_MMAP
+ 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;
+#endif
default:
- ret = -EINVAL;
+ ret = -ENOTTY;
break;
}
mutex_unlock(&dmxdev->mutex);
@@ -1177,28 +1334,56 @@ static long dvb_dvr_ioctl(struct file *file,
return dvb_usercopy(file, cmd, arg, dvb_dvr_do_ioctl);
}
-static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait)
+static __poll_t dvb_dvr_poll(struct file *file, poll_table *wait)
{
struct dvb_device *dvbdev = file->private_data;
struct dmxdev *dmxdev = dvbdev->priv;
- unsigned int mask = 0;
+ __poll_t mask = 0;
- dprintk("function : %s\n", __func__);
+ dprintk("%s\n", __func__);
poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
- if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+ if (dmxdev->exit)
+ return EPOLLERR;
+ if (dvb_vb2_is_streaming(&dmxdev->dvr_vb2_ctx))
+ return dvb_vb2_poll(&dmxdev->dvr_vb2_ctx, file, wait);
+
+ if (((file->f_flags & O_ACCMODE) == O_RDONLY) ||
+ dmxdev->may_do_mmap) {
if (dmxdev->dvr_buffer.error)
- mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+ mask |= (EPOLLIN | EPOLLRDNORM | EPOLLPRI | EPOLLERR);
if (!dvb_ringbuffer_empty(&dmxdev->dvr_buffer))
- mask |= (POLLIN | POLLRDNORM | POLLPRI);
+ mask |= (EPOLLIN | EPOLLRDNORM | EPOLLPRI);
} else
- mask |= (POLLOUT | POLLWRNORM | POLLPRI);
+ mask |= (EPOLLOUT | EPOLLWRNORM | EPOLLPRI);
return mask;
}
+#ifdef CONFIG_DVB_MMAP
+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->may_do_mmap)
+ return -ENOTTY;
+
+ 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;
+}
+#endif
+
static const struct file_operations dvb_dvr_fops = {
.owner = THIS_MODULE,
.read = dvb_dvr_read,
@@ -1208,23 +1393,29 @@ static const struct file_operations dvb_dvr_fops = {
.release = dvb_dvr_release,
.poll = dvb_dvr_poll,
.llseek = default_llseek,
+#ifdef CONFIG_DVB_MMAP
+ .mmap = dvb_dvr_mmap,
+#endif
};
-static struct dvb_device dvbdev_dvr = {
+static const struct dvb_device dvbdev_dvr = {
.priv = NULL,
.readers = 1,
.users = 1,
+#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
+ .name = "dvb-dvr",
+#endif
.fops = &dvb_dvr_fops
};
-
int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
{
- int i;
+ int i, ret;
if (dmxdev->demux->open(dmxdev->demux) < 0)
return -EUSERS;
- dmxdev->filter = vmalloc(dmxdev->filternum * sizeof(struct dmxdev_filter));
+ dmxdev->filter = vmalloc_array(dmxdev->filternum,
+ sizeof(struct dmxdev_filter));
if (!dmxdev->filter)
return -ENOMEM;
@@ -1237,28 +1428,43 @@ int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
DMXDEV_STATE_FREE);
}
- dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev,
- DVB_DEVICE_DEMUX);
- dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr,
- dmxdev, DVB_DEVICE_DVR);
+ ret = dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev,
+ DVB_DEVICE_DEMUX, dmxdev->filternum);
+ if (ret < 0)
+ goto err_register_dvbdev;
+
+ ret = dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr,
+ dmxdev, DVB_DEVICE_DVR, dmxdev->filternum);
+ if (ret < 0)
+ goto err_register_dvr_dvbdev;
dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192);
return 0;
+
+err_register_dvr_dvbdev:
+ dvb_unregister_device(dmxdev->dvbdev);
+err_register_dvbdev:
+ vfree(dmxdev->filter);
+ dmxdev->filter = NULL;
+ return ret;
}
EXPORT_SYMBOL(dvb_dmxdev_init);
void dvb_dmxdev_release(struct dmxdev *dmxdev)
{
- dmxdev->exit=1;
+ mutex_lock(&dmxdev->mutex);
+ dmxdev->exit = 1;
+ mutex_unlock(&dmxdev->mutex);
+
if (dmxdev->dvbdev->users > 1) {
wait_event(dmxdev->dvbdev->wait_queue,
- dmxdev->dvbdev->users==1);
+ dmxdev->dvbdev->users == 1);
}
if (dmxdev->dvr_dvbdev->users > 1) {
wait_event(dmxdev->dvr_dvbdev->wait_queue,
- dmxdev->dvr_dvbdev->users==1);
+ dmxdev->dvr_dvbdev->users == 1);
}
dvb_unregister_device(dmxdev->dvbdev);