1481 lines · 35.3 KB · C
RawDownload
// 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 */ #define pr_fmt(fmt) "dmxdev: " fmt #include <linux/sched.h>#include <linux/spinlock.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/module.h>#include <linux/poll.h>#include <linux/ioctl.h>#include <linux/wait.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(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){	ssize_t free; 	if (!len)		return 0;	if (!buf->data)		return 0; 	free = dvb_ringbuffer_free(buf);	if (len > free) {		dprintk("buffer overflow\n");		return -EOVERFLOW;	} 	return dvb_ringbuffer_write(buf, src, len);} static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src,				      int non_blocking, char __user *buf,				      size_t count, loff_t *ppos){	size_t todo;	ssize_t avail;	ssize_t ret = 0; 	if (!src->data)		return 0; 	if (src->error) {		ret = src->error;		dvb_ringbuffer_flush(src);		return ret;	} 	for (todo = count; todo > 0; todo -= ret) {		if (non_blocking && dvb_ringbuffer_empty(src)) {			ret = -EWOULDBLOCK;			break;		} 		ret = wait_event_interruptible(src->queue,					       !dvb_ringbuffer_empty(src) ||					       (src->error != 0));		if (ret < 0)			break; 		if (src->error) {			ret = src->error;			dvb_ringbuffer_flush(src);			break;		} 		avail = dvb_ringbuffer_avail(src);		if (avail > todo)			avail = todo; 		ret = dvb_ringbuffer_read_user(src, buf, avail);		if (ret < 0)			break; 		buf += ret;	} 	return (count - todo) ? (count - todo) : ret;} static struct dmx_frontend *get_fe(struct dmx_demux *demux, int type){	struct list_head *head, *pos; 	head = demux->get_frontends(demux);	if (!head)		return NULL;	list_for_each(pos, head)		if (DMX_FE_ENTRY(pos)->source == type)			return DMX_FE_ENTRY(pos); 	return NULL;} 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("%s\n", __func__); 	if (mutex_lock_interruptible(&dmxdev->mutex))		return -ERESTARTSYS; 	if (dmxdev->exit) {		mutex_unlock(&dmxdev->mutex);		return -ENODEV;	} 	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 (need_ringbuffer) {		void *mem; 		if (!dvbdev->readers) {			mutex_unlock(&dmxdev->mutex);			return -EBUSY;		}		mem = vmalloc(DVR_BUFFER_SIZE);		if (!mem) {			mutex_unlock(&dmxdev->mutex);			return -ENOMEM;		}		dmxdev->dvr_buffer.data = mem;		dmxdev->dvr_buffer.size = DVR_BUFFER_SIZE;		dvb_ringbuffer_reset(&dmxdev->dvr_buffer);		if (dmxdev->may_do_mmap)			dvb_vb2_init(&dmxdev->dvr_vb2_ctx, "dvr",				     &dmxdev->mutex,				     file->f_flags & O_NONBLOCK);		dvbdev->readers--;	} 	if ((file->f_flags & O_ACCMODE) == O_WRONLY) {		dmxdev->dvr_orig_fe = dmxdev->demux->frontend; 		if (!dmxdev->demux->write) {			mutex_unlock(&dmxdev->mutex);			return -EOPNOTSUPP;		} 		front = get_fe(dmxdev->demux, DMX_MEMORY_FE); 		if (!front) {			mutex_unlock(&dmxdev->mutex);			return -EINVAL;		}		dmxdev->demux->disconnect_frontend(dmxdev->demux);		dmxdev->demux->connect_frontend(dmxdev->demux, front);	}	dvbdev->users++;	mutex_unlock(&dmxdev->mutex);	return 0;} static int dvb_dvr_release(struct inode *inode, struct file *file){	struct dvb_device *dvbdev = file->private_data;	struct dmxdev *dmxdev = dvbdev->priv; 	mutex_lock(&dmxdev->mutex); 	if ((file->f_flags & O_ACCMODE) == O_WRONLY) {		dmxdev->demux->disconnect_frontend(dmxdev->demux);		dmxdev->demux->connect_frontend(dmxdev->demux,						dmxdev->dvr_orig_fe);	} 	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;			spin_unlock_irq(&dmxdev->lock);			vfree(mem);		}	}	/* TODO */	dvbdev->users--;	if (dvbdev->users == 1 && dmxdev->exit == 1) {		mutex_unlock(&dmxdev->mutex);		wake_up(&dvbdev->wait_queue);	} else		mutex_unlock(&dmxdev->mutex); 	return 0;} static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,			     size_t count, loff_t *ppos){	struct dvb_device *dvbdev = file->private_data;	struct dmxdev *dmxdev = dvbdev->priv;	int ret; 	if (!dmxdev->demux->write)		return -EOPNOTSUPP;	if ((file->f_flags & O_ACCMODE) != O_WRONLY)		return -EINVAL;	if (mutex_lock_interruptible(&dmxdev->mutex))		return -ERESTARTSYS; 	if (dmxdev->exit) {		mutex_unlock(&dmxdev->mutex);		return -ENODEV;	}	ret = dmxdev->demux->write(dmxdev->demux, buf, count);	mutex_unlock(&dmxdev->mutex);	return ret;} static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count,			    loff_t *ppos){	struct dvb_device *dvbdev = file->private_data;	struct dmxdev *dmxdev = dvbdev->priv; 	if (dmxdev->exit)		return -ENODEV; 	return dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer,				      file->f_flags & O_NONBLOCK,				      buf, count, ppos);} static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,				      unsigned long size){	struct dvb_ringbuffer *buf = &dmxdev->dvr_buffer;	void *newmem;	void *oldmem; 	dprintk("%s\n", __func__); 	if (buf->size == size)		return 0;	if (!size)		return -EINVAL; 	newmem = vmalloc(size);	if (!newmem)		return -ENOMEM; 	oldmem = buf->data; 	spin_lock_irq(&dmxdev->lock);	buf->data = newmem;	buf->size = size; 	/* reset and not flush in case the buffer shrinks */	dvb_ringbuffer_reset(buf);	spin_unlock_irq(&dmxdev->lock); 	vfree(oldmem); 	return 0;} static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter					       *dmxdevfilter, int state){	spin_lock_irq(&dmxdevfilter->dev->lock);	dmxdevfilter->state = state;	spin_unlock_irq(&dmxdevfilter->dev->lock);} static int dvb_dmxdev_set_buffer_size(struct dmxdev_filter *dmxdevfilter,				      unsigned long size){	struct dvb_ringbuffer *buf = &dmxdevfilter->buffer;	void *newmem;	void *oldmem; 	if (buf->size == size)		return 0;	if (!size)		return -EINVAL;	if (dmxdevfilter->state >= DMXDEV_STATE_GO)		return -EBUSY; 	newmem = vmalloc(size);	if (!newmem)		return -ENOMEM; 	oldmem = buf->data; 	spin_lock_irq(&dmxdevfilter->dev->lock);	buf->data = newmem;	buf->size = size; 	/* reset and not flush in case the buffer shrinks */	dvb_ringbuffer_reset(buf);	spin_unlock_irq(&dmxdevfilter->dev->lock); 	vfree(oldmem); 	return 0;} static void dvb_dmxdev_filter_timeout(struct timer_list *t){	struct dmxdev_filter *dmxdevfilter = timer_container_of(dmxdevfilter,								t, timer); 	dmxdevfilter->buffer.error = -ETIMEDOUT;	spin_lock_irq(&dmxdevfilter->dev->lock);	dmxdevfilter->state = DMXDEV_STATE_TIMEDOUT;	spin_unlock_irq(&dmxdevfilter->dev->lock);	wake_up(&dmxdevfilter->buffer.queue);} static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter){	struct dmx_sct_filter_params *para = &dmxdevfilter->params.sec; 	timer_delete(&dmxdevfilter->timer);	if (para->timeout) {		dmxdevfilter->timer.expires =		    jiffies + 1 + (HZ / 2 + HZ * para->timeout) / 1000;		add_timer(&dmxdevfilter->timer);	}} 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,				       u32 *buffer_flags){	struct dmxdev_filter *dmxdevfilter = filter->priv;	int ret; 	if (!dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx) &&	    dmxdevfilter->buffer.error) {		wake_up(&dmxdevfilter->buffer.queue);		return 0;	}	spin_lock(&dmxdevfilter->dev->lock);	if (dmxdevfilter->state != DMXDEV_STATE_GO) {		spin_unlock(&dmxdevfilter->dev->lock);		return 0;	}	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, true);		if (ret == buffer1_len)			ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx,						  buffer2, buffer2_len,						  buffer_flags, true);	} 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;	if (dmxdevfilter->params.sec.flags & DMX_ONESHOT)		dmxdevfilter->state = DMXDEV_STATE_DONE;	spin_unlock(&dmxdevfilter->dev->lock);	wake_up(&dmxdevfilter->buffer.queue);	return 0;} 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,				  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);	if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) {		spin_unlock(&dmxdevfilter->dev->lock);		return 0;	} 	if (dmxdevfilter->params.pes.output == DMX_OUT_TAP ||	    dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP) {		buffer = &dmxdevfilter->buffer;#ifdef CONFIG_DVB_MMAP		ctx = &dmxdevfilter->vb2_ctx;#endif	} else {		buffer = &dmxdevfilter->dev->dvr_buffer;#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, false);		if (ret == buffer1_len)			ret = dvb_vb2_fill_buffer(ctx, buffer2, buffer2_len,						  buffer_flags, false);	} 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);	}	if (ret < 0)		buffer->error = ret;	spin_unlock(&dmxdevfilter->dev->lock);	wake_up(&buffer->queue);	return 0;} /* stop feed but only mark the specified filter as stopped (state set) */static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter){	struct dmxdev_feed *feed; 	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); 	switch (dmxdevfilter->type) {	case DMXDEV_TYPE_SEC:		timer_delete(&dmxdevfilter->timer);		dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec);		break;	case DMXDEV_TYPE_PES:		list_for_each_entry(feed, &dmxdevfilter->feed.ts, next)			feed->ts->stop_filtering(feed->ts);		break;	default:		return -EINVAL;	}	return 0;}

Lines 1–500 of 1481.