⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dmxdev.c

📁 linux内核源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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. * */#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 <asm/uaccess.h>#include <asm/system.h>#include "dmxdev.h"static int debug;module_param(debug, int, 0644);MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");#define dprintk	if (debug) printkstatic 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("dmxdev: 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(src, (u8 *)buf, avail, 1);		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;	dprintk("function : %s\n", __FUNCTION__);	if (mutex_lock_interruptible(&dmxdev->mutex))		return -ERESTARTSYS;	if (dmxdev->exit) {		mutex_unlock(&dmxdev->mutex);		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) {		void *mem;		if (!dvbdev->readers) {			mutex_unlock(&dmxdev->mutex);			return -EBUSY;		}		mem = vmalloc(DVR_BUFFER_SIZE);		if (!mem) {			mutex_unlock(&dmxdev->mutex);			return -ENOMEM;		}		dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE);		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) {		dvbdev->readers++;		if (dmxdev->dvr_buffer.data) {			void *mem = dmxdev->dvr_buffer.data;			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) {		fops_put(file->f_op);		file->f_op = NULL;		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;	int ret;	if (dmxdev->exit) {		mutex_unlock(&dmxdev->mutex);		return -ENODEV;	}	//mutex_lock(&dmxdev->mutex);	ret = dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer,				     file->f_flags & O_NONBLOCK,				     buf, count, ppos);	//mutex_unlock(&dmxdev->mutex);	return ret;}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 *mem;	if (buf->size == size)		return 0;	if (dmxdevfilter->state >= DMXDEV_STATE_GO)		return -EBUSY;	spin_lock_irq(&dmxdevfilter->dev->lock);	mem = buf->data;	buf->data = NULL;	buf->size = size;	dvb_ringbuffer_flush(buf);	spin_unlock_irq(&dmxdevfilter->dev->lock);	vfree(mem);	if (buf->size) {		mem = vmalloc(dmxdevfilter->buffer.size);		if (!mem)			return -ENOMEM;		spin_lock_irq(&dmxdevfilter->dev->lock);		buf->data = mem;		spin_unlock_irq(&dmxdevfilter->dev->lock);	}	return 0;}static void dvb_dmxdev_filter_timeout(unsigned long data){	struct dmxdev_filter *dmxdevfilter = (struct dmxdev_filter *)data;	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;	del_timer(&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);	}}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){	struct dmxdev_filter *dmxdevfilter = filter->priv;	int ret;	if (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;	}	del_timer(&dmxdevfilter->timer);	dprintk("dmxdev: section callback %02x %02x %02x %02x %02x %02x\n",		buffer1[0], buffer1[1],		buffer1[2], buffer1[3], buffer1[4], buffer1[5]);	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) {		dvb_ringbuffer_flush(&dmxdevfilter->buffer);		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,				  enum dmx_success success){	struct dmxdev_filter *dmxdevfilter = feed->priv;	struct dvb_ringbuffer *buffer;	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)		buffer = &dmxdevfilter->buffer;	else		buffer = &dmxdevfilter->dev->dvr_buffer;	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) {		dvb_ringbuffer_flush(buffer);		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){	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);	switch (dmxdevfilter->type) {	case DMXDEV_TYPE_SEC:		del_timer(&dmxdevfilter->timer);		dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec);		break;	case DMXDEV_TYPE_PES:		dmxdevfilter->feed.ts->stop_filtering(dmxdevfilter->feed.ts);		break;	default:		return -EINVAL;	}	return 0;}/* start feed associated with the specified filter */static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter){	dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);	switch (filter->type) {	case DMXDEV_TYPE_SEC:		return filter->feed.sec->start_filtering(filter->feed.sec);	case DMXDEV_TYPE_PES:		return filter->feed.ts->start_filtering(filter->feed.ts);	default:		return -EINVAL;	}	return 0;}/* restart section feed if it has filters left associated with it,   otherwise release the feed */static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter){	int i;	struct dmxdev *dmxdev = filter->dev;	u16 pid = filter->params.sec.pid;	for (i = 0; i < dmxdev->filternum; i++)		if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&		    dmxdev->filter[i].type == DMXDEV_TYPE_SEC &&		    dmxdev->filter[i].params.sec.pid == pid) {			dvb_dmxdev_feed_start(&dmxdev->filter[i]);			return 0;		}	filter->dev->demux->release_section_feed(dmxdev->demux,						 filter->feed.sec);	return 0;}static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter){	if (dmxdevfilter->state < DMXDEV_STATE_GO)		return 0;	switch (dmxdevfilter->type) {	case DMXDEV_TYPE_SEC:		if (!dmxdevfilter->feed.sec)			break;		dvb_dmxdev_feed_stop(dmxdevfilter);		if (dmxdevfilter->filter.sec)			dmxdevfilter->feed.sec->			    release_filter(dmxdevfilter->feed.sec,					   dmxdevfilter->filter.sec);		dvb_dmxdev_feed_restart(dmxdevfilter);		dmxdevfilter->feed.sec = NULL;		break;	case DMXDEV_TYPE_PES:		if (!dmxdevfilter->feed.ts)			break;		dvb_dmxdev_feed_stop(dmxdevfilter);		dmxdevfilter->dev->demux->		    release_ts_feed(dmxdevfilter->dev->demux,				    dmxdevfilter->feed.ts);		dmxdevfilter->feed.ts = NULL;		break;	default:		if (dmxdevfilter->state == DMXDEV_STATE_ALLOCATED)			return 0;		return -EINVAL;	}	dvb_ringbuffer_flush(&dmxdevfilter->buffer);	return 0;}static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter){	if (dmxdevfilter->state < DMXDEV_STATE_SET)		return 0;	dmxdevfilter->type = DMXDEV_TYPE_NONE;	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);	return 0;}static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter){	struct dmxdev *dmxdev = filter->dev;	void *mem;	int ret, i;	if (filter->state < DMXDEV_STATE_SET)		return -EINVAL;	if (filter->state >= DMXDEV_STATE_GO)		dvb_dmxdev_filter_stop(filter);	if (!filter->buffer.data) {		mem = vmalloc(filter->buffer.size);		if (!mem)			return -ENOMEM;		spin_lock_irq(&filter->dev->lock);		filter->buffer.data = mem;		spin_unlock_irq(&filter->dev->lock);	}	dvb_ringbuffer_flush(&filter->buffer);	switch (filter->type) {	case DMXDEV_TYPE_SEC:	{		struct dmx_sct_filter_params *para = &filter->params.sec;		struct dmx_section_filter **secfilter = &filter->filter.sec;		struct dmx_section_feed **secfeed = &filter->feed.sec;		*secfilter = NULL;		*secfeed = NULL;		/* find active filter/feed with same PID */		for (i = 0; i < dmxdev->filternum; i++) {			if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&			    dmxdev->filter[i].type == DMXDEV_TYPE_SEC &&			    dmxdev->filter[i].params.sec.pid == para->pid) {				*secfeed = dmxdev->filter[i].feed.sec;				break;			}		}		/* if no feed found, try to allocate new one */		if (!*secfeed) {			ret = dmxdev->demux->allocate_section_feed(dmxdev->demux,								   secfeed,								   dvb_dmxdev_section_callback);			if (ret < 0) {				printk("DVB (%s): could not alloc feed\n",				       __FUNCTION__);				return ret;			}			ret = (*secfeed)->set(*secfeed, para->pid, 32768,					      (para->flags & DMX_CHECK_CRC) ? 1 : 0);			if (ret < 0) {				printk("DVB (%s): could not set feed\n",				       __FUNCTION__);				dvb_dmxdev_feed_restart(filter);				return ret;			}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -