File:  [DVB] / margi2 / dvb_demux.c
Revision 1.4: download - view: text, annotated - select for diffs
Thu Mar 29 23:12:01 2001 UTC (23 years, 2 months ago) by mocm
Branches: MAIN
CVS tags: HEAD
- improved playback on slow machines
- /dev/ost/dvr accepts TS without crashing
- section filters work
- only playback doesn't work (no picture and sound)

/* 
 * dvb_demux.c - DVB kernel demux API
 *
 * Copyright (C) 2000-2001 Ralph  Metzler <ralph@convergence.de>
 *                       & Marcus Metzler <marcus@convergence.de>
 *                         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/vmalloc.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <asm/uaccess.h>

#include "dvb_demux.h"

LIST_HEAD(dmx_muxs);

int dmx_register_demux(dmx_demux_t *demux) 
{
	struct list_head *pos, *head=&dmx_muxs;
	
	if (!(demux->id && demux->vendor && demux->model)) 
	        return -EINVAL;
	list_for_each(pos, head) 
	{
	        if (!strcmp(DMX_DIR_ENTRY(pos)->id, demux->id))
		        return -EEXIST;
	}
	
	demux->users=0;
	list_add(&(demux->reg_list), head);
	MOD_INC_USE_COUNT;
	return 0;
}

int dmx_unregister_demux(dmx_demux_t* demux)
{
	struct list_head *pos, *head=&dmx_muxs;

	list_for_each(pos, head) 
	{
	        if (DMX_DIR_ENTRY(pos)==demux) 
		{
		        if (demux->users!=0)
			        return -EINVAL;
		        list_del(pos);
		        MOD_DEC_USE_COUNT;
			return 0;
		}
	}
	return -ENODEV;
}


struct list_head *dmx_get_demuxes(void)
{
        if (list_empty(&dmx_muxs))
	        return NULL;

	return &dmx_muxs;
}

/******************************************************************************
 * static inlined helper functions
 ******************************************************************************/

static inline u16 
section_length(const u8 *buf)
{
        return 3+((buf[1]&0x0f)<<8)+buf[2];
}

static inline u16 
ts_pid(const u8 *buf)
{
        return ((buf[1]&0x1f)<<8)+buf[2];
}

static inline int
payload(const u8 *tsp)
{
        if (!(tsp[3]&0x10)) // no payload?
                return 0;
        if (tsp[3]&0x20)   // adaptation field?
                return 184-1-tsp[4];
        return 184;
}

/******************************************************************************
 * Software filter functions
 ******************************************************************************/

static inline int
DvbDmxSWFilterPayload(dvb_demux_feed_t *dvbdmxfeed, const u8 *buf) 
{
        int p, count, ccok;

        if (!(count=payload(buf)))
                return -1;
        p=188-count;
        ccok=((dvbdmxfeed->cc+1)&0x0f)==(buf[3]&0x0f) ? 1 : 0;
        dvbdmxfeed->cc=buf[3]&0x0f;
        /*
        if (!ccok)
          printk("missed packet!\n");
        */
        if (buf[1]&0x40)  // PUSI ?
                dvbdmxfeed->peslen=0xfffa;
        dvbdmxfeed->peslen+=count;

        return dvbdmxfeed->cb.ts((u8 *)&buf[p], count, 0, 0, 
                                 &dvbdmxfeed->feed.ts, DMX_OK); 
}


static int
DvbDmxSWFilterSectionFilter(dvb_demux_feed_t *dvbdmxfeed, 
                            dvb_demux_filter_t *dvbdmxfilter)
{
        dmx_section_filter_t *filter=&dvbdmxfilter->filter;
#if 0
        int i;

        for (i=0; i<DVB_DEMUX_MASK_MAX; i++)
                if (filter->filter_mask[i]&
                    (filter->filter_value[i]^dvbdmxfeed->secbuf[i]))
                        return 0;
#else
        u32 res;
        u32 *val=(u32 *)(filter->filter_value);
        u32 *mask=(u32 *)(filter->filter_mask);
        u32 *data=(u32 *)(dvbdmxfeed->secbuf);

        res=mask[0]&(val[0]^data[0]);
        if (res) return 0;

        res=mask[1]&(val[1]^data[1]);
        if (res) return 0;

        res=mask[2]&(val[2]^data[2]);
        if (res) return 0;

        res=mask[3]&(val[3]^data[3]);
        if (res) return 0;

        res=*(u16 *)(4+mask) & (*(u16 *)(4+val) ^ *(u16 *)(4+data));
        if (res) return 0;
#endif

        return dvbdmxfeed->cb.sec(dvbdmxfeed->secbuf, dvbdmxfeed->seclen, 
                                  0, 0, filter, DMX_OK); 
}

static inline int
DvbDmxSWFilterSectionFeed(dvb_demux_feed_t *dvbdmxfeed)
{
        u8 *buf=dvbdmxfeed->secbuf;
        dvb_demux_filter_t *f;

        if (dvbdmxfeed->secbufp!=dvbdmxfeed->seclen)
                return -1;
        if (!dvbdmxfeed->feed.sec.is_filtering)
                return 0;
        if (!(f=dvbdmxfeed->filter))
                return 0;
        do 
                if (DvbDmxSWFilterSectionFilter(dvbdmxfeed, f)<0)
                        return -1;
        while ((f=f->next) && dvbdmxfeed->feed.sec.is_filtering);

        dvbdmxfeed->secbufp=dvbdmxfeed->seclen=0;
        memset(buf, 0, DVB_DEMUX_MASK_MAX);
        return 0;
}

static inline int
DvbDmxSWFilterSectionPacket(dvb_demux_feed_t *dvbdmxfeed, const u8 *buf) 
{
        int p, count;
        int ccok, rest;

        if (!(count=payload(buf)))
                return -1;
        p=188-count;

        ccok=((dvbdmxfeed->cc+1)&0x0f)==(buf[3]&0x0f) ? 1 : 0;
        dvbdmxfeed->cc=buf[3]&0x0f;

        if (buf[1]&0x40) { // PUSI set
                // offset to start of first section is in buf[p] 
                if (buf[p] && ccok) { // rest of previous section?

                        // did we have enough data in last packet to calc length?
                        if (dvbdmxfeed->secbufp && dvbdmxfeed->secbufp<3) {
                                memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp,
                                       buf+p+1, 
                                       3-dvbdmxfeed->secbufp);
                                dvbdmxfeed->seclen=section_length(dvbdmxfeed->secbuf);
                        }
                        rest=dvbdmxfeed->seclen-dvbdmxfeed->secbufp;
                        if (rest==buf[p] && dvbdmxfeed->seclen) {
                                memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp,
                                       buf+p+1, buf[p]);
                                dvbdmxfeed->secbufp+=buf[p];
                                DvbDmxSWFilterSectionFeed(dvbdmxfeed);
                        }
                }
                p+=buf[p]+1; 		// skip rest of last section
                count=188-p;
                while (count>0) {
                        if ((count>2) &&  // enough data to determine sec length?
                            ((dvbdmxfeed->seclen=section_length(buf+p))<=count)) {
                                memcpy(dvbdmxfeed->secbuf, buf+p, 
                                       dvbdmxfeed->seclen);
                                dvbdmxfeed->secbufp=dvbdmxfeed->seclen;
                                p+=dvbdmxfeed->seclen;
                                count=188-p;
                                DvbDmxSWFilterSectionFeed(dvbdmxfeed);

                                // filling bytes until packet end?
                                if (buf[p]==0xff) 
                                        count=0;
                        } else { // section continues to following ts block
                                memcpy(dvbdmxfeed->secbuf, buf+p, count);
                                dvbdmxfeed->secbufp+=count;
                                count=0;
                        }
                }
        } else {		// section continued below
                if (!ccok)
                        return -1;
                if (!dvbdmxfeed->secbufp) // any data in last ts packet?
                        return -1;
                // did we have enough data in last packet to calc section length?
                if (dvbdmxfeed->secbufp<3) {
                        memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp, buf+p, 
                               3-dvbdmxfeed->secbufp);
                        dvbdmxfeed->seclen=section_length(dvbdmxfeed->secbuf);
                }
                rest=dvbdmxfeed->seclen-dvbdmxfeed->secbufp;
                if (rest<0)
                        return -1;
		if (rest<=count) {	// section completed in this ts block
	                memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp, buf+p, rest);
        	        dvbdmxfeed->secbufp+=rest;
			DvbDmxSWFilterSectionFeed(dvbdmxfeed);
		} else 	{	// section continues in following ts block
                        memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp, buf+p, count);
                        dvbdmxfeed->secbufp+=count;
		}

        }
        return 0;
}

static void 
DvbDmxSWFilterPacketType(dvb_demux_feed_t *dvbdmxfeed, const u8 *buf)
{
        switch(dvbdmxfeed->type) {
        case DMX_TYPE_TS:
                if (!dvbdmxfeed->feed.ts.is_filtering)
                        break;
                if (dvbdmxfeed->ts_type & TS_PACKET) {
                        if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY)
                                DvbDmxSWFilterPayload(dvbdmxfeed, buf);
                        else
                                dvbdmxfeed->cb.ts((u8 *)buf, 188, 0, 0, 
                                                  &dvbdmxfeed->feed.ts, DMX_OK); 
                }
                if (dvbdmxfeed->ts_type & TS_DECODER) 
                        if (dvbdmxfeed->demux->write_to_decoder)
                                dvbdmxfeed->demux->
                                  write_to_decoder(dvbdmxfeed, (u8 *)buf, 188);
                break;

        case DMX_TYPE_SEC:
                if (!dvbdmxfeed->feed.sec.is_filtering)
                        break;
                if (DvbDmxSWFilterSectionPacket(dvbdmxfeed, buf)<0)
                        dvbdmxfeed->seclen=dvbdmxfeed->secbufp=0;
                break;

        default:
                break;
        }
}

void inline
DvbDmxSWFilterPacket(dvb_demux_t *dvbdmx, const u8 *buf)
{
        dvb_demux_feed_t *dvbdmxfeed;

        if (!(dvbdmxfeed=dvbdmx->pid2feed[ts_pid(buf)]))
                return;
        DvbDmxSWFilterPacketType(dvbdmxfeed, buf);
}

void
DvbDmxSWFilterPackets(dvb_demux_t *dvbdmx, const u8 *buf, int count)
{
        dvb_demux_feed_t *dvbdmxfeed;

        for (; count>0; count--, buf+=188)
                if ((dvbdmxfeed=dvbdmx->pid2feed[ts_pid(buf)]))
                        DvbDmxSWFilterPacketType(dvbdmxfeed, buf);
}

static inline void
DvbDmxSWFilter(dvb_demux_t *dvbdmx, const u8 *buf, size_t count)
{
        int p=0,i, j;
        
        if ((i=dvbdmx->tsbufp)) {
                if (count<(j=188-i)) {
                        memcpy(&dvbdmx->tsbuf[i], buf, count);
                        dvbdmx->tsbufp+=count;
                        return;
                }
                memcpy(&dvbdmx->tsbuf[i], buf, j);
                DvbDmxSWFilterPacket(dvbdmx, dvbdmx->tsbuf);
                dvbdmx->tsbufp=0;
                p+=j;
        }
        
        while (p<count) {
                if (buf[p]==0x47) {
                        if (count-p>=188) {
                                DvbDmxSWFilterPacket(dvbdmx, buf+p);
                                p+=188;
                        } else {
                                i=count-p;
                                memcpy(dvbdmx->tsbuf, buf+p, i);
                                dvbdmx->tsbufp=i;
                                return;
                        }
                } else 
                        p++;
        }          
}


/******************************************************************************
 ******************************************************************************
 * DVB DEMUX API LEVEL FUNCTIONS
 ******************************************************************************
 ******************************************************************************/

static dvb_demux_filter_t *
DvbDmxFilterAlloc(dvb_demux_t *dvbdmx)
{
        int i;

        for (i=0; i<dvbdmx->filternum; i++)
                if (dvbdmx->filter[i].state==DMX_STATE_FREE)
                        break;
        if (i==dvbdmx->filternum)  
                return 0;
        dvbdmx->filter[i].state=DMX_STATE_ALLOCATED;
        return &dvbdmx->filter[i];
}

static dvb_demux_feed_t *
DvbDmxFeedAlloc(dvb_demux_t *dvbdmx)
{
        int i;

        for (i=0; i<dvbdmx->feednum; i++)
                if (dvbdmx->feed[i].state==DMX_STATE_FREE)
                        break;
        if (i==dvbdmx->feednum)
                return 0;
        dvbdmx->feed[i].state=DMX_STATE_ALLOCATED;
        return &dvbdmx->feed[i];
}


/******************************************************************************
 * dmx_ts_feed API calls
 ******************************************************************************/

static int 
dmx_ts_feed_set_type(dmx_ts_feed_t *feed, int type, dmx_ts_pes_t pes_type)
{
        dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
        dvb_demux_t *dvbdmx=dvbdmxfeed->demux;
  
        dvbdmxfeed->ts_type=type;
        dvbdmxfeed->pes_type=pes_type;

        if (dvbdmxfeed->ts_type & TS_DECODER) {
                if (pes_type >= DMX_TS_PES_OTHER)
                        return -EINVAL;
                if (dvbdmx->pesfilter[pes_type] && 
                    (dvbdmx->pesfilter[pes_type]!=dvbdmxfeed))
                        return -EINVAL;
                dvbdmx->pesfilter[pes_type]=dvbdmxfeed;
                //printk("pids[%d]=%04x\n", pes_type, dvbdmxfeed->pid);
	        dvbdmx->pids[pes_type]=dvbdmxfeed->pid;
        }
        return 0;
}

static int 
dmx_ts_feed_set(struct dmx_ts_feed_s* feed,
                __u16 pid,
                size_t callback_length, 
                size_t circular_buffer_size, 
                int descramble, 
                struct timespec timeout
                )
{
        dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
        dvb_demux_t *dvbdmx=dvbdmxfeed->demux;

        if (pid>0x1fff)
                return -EINVAL;
        if (dvbdmxfeed->pid!=0xffff) {
                if (dvbdmxfeed->pid<=0x1fff)
                        dvbdmx->pid2feed[dvbdmxfeed->pid]=0;
                dvbdmxfeed->pid=0xffff;
        }
        if (dvbdmx->pid2feed[pid])
                return -EBUSY;

        dvbdmx->pid2feed[pid]=dvbdmxfeed;
        dvbdmxfeed->pid=pid;

        dvbdmxfeed->buffer_size=circular_buffer_size;
        dvbdmxfeed->descramble=descramble;
        dvbdmxfeed->timeout=timeout;
        dvbdmxfeed->cb_length=callback_length;
        dvbdmxfeed->ts_type=TS_PACKET;

        if (dvbdmxfeed->descramble)
                return -ENOSYS;

        if (dvbdmxfeed->buffer_size) {
                dvbdmxfeed->buffer=vmalloc(dvbdmxfeed->buffer_size);
                if (!dvbdmxfeed->buffer)
                        return -ENOMEM;
        }
        dvbdmxfeed->state=DMX_STATE_READY;
        return 0;
}

static int 
dmx_ts_feed_start_filtering(struct dmx_ts_feed_s* feed)
{
        dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
        dvb_demux_t *dvbdmx=dvbdmxfeed->demux;
	int ret;

        if (dvbdmxfeed->state!=DMX_STATE_READY)
                return -EINVAL;
        if (dvbdmxfeed->type!=DMX_TYPE_TS)
                return -EINVAL;

        if (!dvbdmx->start_feed)
                return -1;
        ret=dvbdmx->start_feed(dvbdmxfeed); 
        if (ret<0) 
              return ret;
        feed->is_filtering=1;
        dvbdmxfeed->state=DMX_STATE_GO;
	return 0;
}
 
static int 
dmx_ts_feed_stop_filtering(struct dmx_ts_feed_s* feed)
{
        dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
        dvb_demux_t *dvbdmx=dvbdmxfeed->demux;
	int ret;

        //printk ("function : %s\n", __FUNCTION__);
        if (dvbdmxfeed->state<DMX_STATE_GO)
                return -EINVAL;
        if (!dvbdmx->stop_feed)
                return -1;
        ret=dvbdmx->stop_feed(dvbdmxfeed); 
        feed->is_filtering=0;
        dvbdmxfeed->state=DMX_STATE_ALLOCATED;

        return ret;
}

static int dvbdmx_allocate_ts_feed(dmx_demux_t *demux,
                                   dmx_ts_feed_t **feed, 
                                   dmx_ts_cb callback)
{
        dvb_demux_t *dvbdmx=(dvb_demux_t *) demux;
        dvb_demux_feed_t *dvbdmxfeed;

        if (!(dvbdmxfeed=DvbDmxFeedAlloc(dvbdmx)))
                return -EBUSY;

        dvbdmxfeed->type=DMX_TYPE_TS;
        dvbdmxfeed->cb.ts=callback;
        dvbdmxfeed->demux=dvbdmx;
        dvbdmxfeed->pid=0xffff;
        dvbdmxfeed->peslen=0xfffa;

        (*feed)=&dvbdmxfeed->feed.ts;
        (*feed)->is_filtering=0;
        (*feed)->parent=demux;
        (*feed)->priv=0;
        (*feed)->set=dmx_ts_feed_set;
        (*feed)->set_type=dmx_ts_feed_set_type;
        (*feed)->start_filtering=dmx_ts_feed_start_filtering;
        (*feed)->stop_filtering=dmx_ts_feed_stop_filtering;


        if (!(dvbdmxfeed->filter=DvbDmxFilterAlloc(dvbdmx))) {
                dvbdmxfeed->state=DMX_STATE_FREE;
                return -EBUSY;
        }

        dvbdmxfeed->filter->type=DMX_TYPE_TS;
        dvbdmxfeed->filter->feed=dvbdmxfeed;
        dvbdmxfeed->filter->state=DMX_STATE_READY;
        
        return 0;
}

static int dvbdmx_release_ts_feed(dmx_demux_t *demux, dmx_ts_feed_t *feed)
{
        dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;

        if (dvbdmxfeed->state==DMX_STATE_FREE)
                return -EINVAL;
        if (dvbdmxfeed->buffer) { 
                vfree(dvbdmxfeed->buffer);
                dvbdmxfeed->buffer=0;
        }
        dvbdmxfeed->state=DMX_STATE_FREE;
        dvbdmxfeed->filter->state=DMX_STATE_FREE;
        if (dvbdmxfeed->pid!=0xffff) {
                if (dvbdmxfeed->pid<=0x1fff) 
                        dvbdmxfeed->demux->pid2feed[dvbdmxfeed->pid]=0;
                dvbdmxfeed->pid=0xffff;
        }

        return 0;
}


/******************************************************************************
 * dmx_pes_feed API calls
 ******************************************************************************/
/*
static int 
dmx_pes_feed_set(struct dmx_pes_feed_s* feed,
                 __u16 pid,
                 size_t circular_buffer_size, 
                 int descramble, 
                 struct timespec timeout)
{
        return 0;
}

static int 
dmx_pes_feed_start_filtering(struct dmx_pes_feed_s* feed)
{
        return 0;
}
 
static int 
dmx_pes_feed_stop_filtering(struct dmx_pes_feed_s* feed)
{
        return 0;
}
*/

static int dvbdmx_allocate_pes_feed(dmx_demux_t *demux, 
                                    dmx_pes_feed_t **feed,
                                    dmx_pes_cb callback)
{
        return 0;
}

static int dvbdmx_release_pes_feed(dmx_demux_t *demux, 
                                   dmx_pes_feed_t *feed)
{
        return 0;
}


/******************************************************************************
 * dmx_section_feed API calls
 ******************************************************************************/

static int 
dmx_section_feed_allocate_filter(struct dmx_section_feed_s* feed, 
                                 dmx_section_filter_t** filter) 
{
        dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
        dvb_demux_t *dvbdemux=dvbdmxfeed->demux;
        dvb_demux_filter_t *dvbdmxfilter;

        dvbdmxfilter=DvbDmxFilterAlloc(dvbdemux);
        if (!dvbdmxfilter)
                return -ENOSPC;

        *filter=&dvbdmxfilter->filter;
        (*filter)->parent=feed;
        (*filter)->priv=0;
        dvbdmxfilter->feed=dvbdmxfeed;
        dvbdmxfilter->pid=dvbdmxfeed->pid; 
        dvbdmxfilter->type=DMX_TYPE_SEC;
        dvbdmxfilter->state=DMX_STATE_READY;

        dvbdmxfilter->next=dvbdmxfeed->filter;
        dvbdmxfeed->filter=dvbdmxfilter;
        return 0;
}

static int 
dmx_section_feed_set(struct dmx_section_feed_s* feed, 
                     __u16 pid, size_t circular_buffer_size, 
                     int descramble, int check_crc) 
{
        dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
        dvb_demux_t *dvbdmx=dvbdmxfeed->demux;

        //printk ("function : %s  PID %04x\n", __FUNCTION__, pid);

        if (pid>0x1fff)
                return -EINVAL;
        if (dvbdmxfeed->pid!=0xffff) {
                dvbdmx->pid2feed[dvbdmxfeed->pid]=0;
                dvbdmxfeed->pid=0xffff;
        }
        if (dvbdmx->pid2feed[pid])
                return -EBUSY;
        dvbdmx->pid2feed[pid]=dvbdmxfeed;

        dvbdmxfeed->pid=pid;
        dvbdmxfeed->buffer_size=circular_buffer_size;
        dvbdmxfeed->descramble=descramble;
        if (dvbdmxfeed->descramble)
                return -ENOSYS;

        dvbdmxfeed->check_crc=check_crc;
        dvbdmxfeed->buffer=vmalloc(dvbdmxfeed->buffer_size);
        if (!dvbdmxfeed->buffer)
                return -ENOMEM;
        dvbdmxfeed->state=DMX_STATE_READY;
        return 0;
}

static int 
dmx_section_feed_start_filtering(dmx_section_feed_t *feed)
{
        dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
        dvb_demux_t *dvbdmx=dvbdmxfeed->demux;
	int ret;

        if (feed->is_filtering)
                return -EBUSY;
        if (!dvbdmxfeed->filter)
                return -EINVAL;

        dvbdmxfeed->secbufp=0;
        dvbdmxfeed->seclen=0;
        
        if (!dvbdmx->start_feed)
                return -1;
        ret=dvbdmx->start_feed(dvbdmxfeed); 
	if (ret<0) 
	        return ret;
        feed->is_filtering=1;
        dvbdmxfeed->state=DMX_STATE_GO;
	return 0;
}

static int 
dmx_section_feed_stop_filtering(struct dmx_section_feed_s* feed)
{
        dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
        dvb_demux_t *dvbdmx=dvbdmxfeed->demux;
        int ret;

        if (!dvbdmx->stop_feed)
                return -1;
	ret=dvbdmx->stop_feed(dvbdmxfeed); 

        dvbdmxfeed->state=DMX_STATE_READY;
        feed->is_filtering=0;
	return ret;
}

static int 
dmx_section_feed_release_filter(dmx_section_feed_t *feed, 
                                dmx_section_filter_t* filter)
{
        dvb_demux_filter_t *dvbdmxfilter=(dvb_demux_filter_t *) filter, *f;
        dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;

        if (dvbdmxfilter->feed!=dvbdmxfeed)
                return -EINVAL;
        if (feed->is_filtering) 
                feed->stop_filtering(feed);
                //return -EBUSY;

        f=dvbdmxfeed->filter;
        if (f==dvbdmxfilter)
                dvbdmxfeed->filter=dvbdmxfilter->next;
        else {
                while(f->next!=dvbdmxfilter)
                        f=f->next;
                f->next=f->next->next;
        }

        dvbdmxfilter->state=DMX_STATE_FREE;
        return 0;
}

static int dvbdmx_allocate_section_feed(dmx_demux_t *demux, 
                                        dmx_section_feed_t **feed,
                                        dmx_section_cb callback)
{
        dvb_demux_t *dvbdmx=(dvb_demux_t *) demux;
        dvb_demux_feed_t *dvbdmxfeed;

        if (!(dvbdmxfeed=DvbDmxFeedAlloc(dvbdmx)))
                return -EBUSY;
        dvbdmxfeed->type=DMX_TYPE_SEC;
        dvbdmxfeed->cb.sec=callback;
        dvbdmxfeed->demux=dvbdmx;
        dvbdmxfeed->pid=0xffff;
        dvbdmxfeed->secbufp=0;
        dvbdmxfeed->filter=0;

        (*feed)=&dvbdmxfeed->feed.sec;
        (*feed)->is_filtering=0;
        (*feed)->parent=demux;
        (*feed)->priv=0;
        (*feed)->set=dmx_section_feed_set;
        (*feed)->allocate_filter=dmx_section_feed_allocate_filter;
        (*feed)->release_filter=dmx_section_feed_release_filter;
        (*feed)->start_filtering=dmx_section_feed_start_filtering;
        (*feed)->stop_filtering=dmx_section_feed_stop_filtering;
        return 0;
}

static int dvbdmx_release_section_feed(dmx_demux_t *demux, 
                                       dmx_section_feed_t *feed)
{
        dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;

        if (dvbdmxfeed->state==DMX_STATE_FREE)
                return -EINVAL;
        if (dvbdmxfeed->buffer) {
                vfree(dvbdmxfeed->buffer);
                dvbdmxfeed->buffer=0;
        }
        dvbdmxfeed->state=DMX_STATE_FREE;
        dvbdmxfeed->demux->pid2feed[dvbdmxfeed->pid]=0;
        if (dvbdmxfeed->pid!=0xffff)
                dvbdmxfeed->demux->pid2feed[dvbdmxfeed->pid]=0;
        return 0;
}


/******************************************************************************
 * dvb_demux kernel data API calls
 ******************************************************************************/

static int dvbdmx_open(dmx_demux_t *demux)
{
        dvb_demux_t *dvbdemux=(dvb_demux_t *) demux;

        if (dvbdemux->users>=MAX_DVB_DEMUX_USERS)
                return -EUSERS;
        dvbdemux->users++;
        return 0;
}

static int dvbdmx_close(struct dmx_demux_s *demux)
{
        dvb_demux_t *dvbdemux=(dvb_demux_t *) demux;

        if (dvbdemux->users==0)
                return -ENODEV;
        dvbdemux->users--;
        //FIXME: release any unneeded resources if users==0
        return 0;
}

static int dvbdmx_write(dmx_demux_t *demux, const char *buf, size_t count)
{
        dvb_demux_t *dvbdemux=(dvb_demux_t *) demux;

        if ((!demux->frontend) ||
            (demux->frontend->source!=DMX_MEMORY_FE))
                return -EINVAL;

        DvbDmxSWFilter(dvbdemux, buf, count);
        return count;
}


static int dvbdmx_add_frontend(dmx_demux_t *demux, 
                               dmx_frontend_t *frontend)
{
        dvb_demux_t *dvbdemux=(dvb_demux_t *) demux;
        struct list_head *pos, *head=&dvbdemux->frontend_list;
	
        //printk ("function : %s\n", __FUNCTION__);

	if (!(frontend->id && frontend->vendor && frontend->model)) 
	        return -EINVAL;
	list_for_each(pos, head) 
	{
	        if (!strcmp(DMX_FE_ENTRY(pos)->id, frontend->id))
		        return -EEXIST;
	}

	list_add(&(frontend->connectivity_list), head);
        return 0;
}

static int 
dvbdmx_remove_frontend(dmx_demux_t *demux, 
                       dmx_frontend_t *frontend)
{
        dvb_demux_t *dvbdemux=(dvb_demux_t *) demux;
        struct list_head *pos, *head=&dvbdemux->frontend_list;

	list_for_each(pos, head) 
	{
	        if (DMX_FE_ENTRY(pos)==frontend) 
                {
		        list_del(pos);
			return 0;
		}
	}
	return -ENODEV;
}

static struct list_head *
dvbdmx_get_frontends(dmx_demux_t *demux)
{
        dvb_demux_t *dvbdemux=(dvb_demux_t *) demux;

        if (list_empty(&dvbdemux->frontend_list))
	        return NULL;
        return &dvbdemux->frontend_list;
}

static int dvbdmx_connect_frontend(dmx_demux_t *demux, 
                                   dmx_frontend_t *frontend)
{
        if (demux->frontend)
                return -EINVAL;
        
        demux->frontend=frontend;
        return 0;
}

static int dvbdmx_disconnect_frontend(dmx_demux_t *demux)
{
        demux->frontend=NULL;
        return 0;
}

static int dvbdmx_get_pes_pids(dmx_demux_t *demux, __u16 *pids)
{
        dvb_demux_t *dvbdemux=(dvb_demux_t *) demux;

        memcpy(pids, dvbdemux->pids, 5*sizeof(__u16));
        return 0;
}

int 
DvbDmxInit(dvb_demux_t *dvbdemux)
{
        int i;
        dmx_demux_t *dmx=&dvbdemux->dmx;

        dvbdemux->users=0;
	dvbdemux->filter=vmalloc(dvbdemux->filternum*sizeof(dvb_demux_filter_t));
	if (!dvbdemux->filter)
                return -ENOMEM;

	dvbdemux->feed=vmalloc(dvbdemux->feednum*sizeof(dvb_demux_feed_t));
	if (!dvbdemux->feed) {
	        vfree(dvbdemux->filter);
                return -ENOMEM;
	}
        for (i=0; i<dvbdemux->filternum; i++) {
                dvbdemux->filter[i].state=DMX_STATE_FREE;
                dvbdemux->filter[i].index=i;
        }
        for (i=0; i<dvbdemux->feednum; i++)
                dvbdemux->feed[i].state=DMX_STATE_FREE;
        dvbdemux->frontend_list.next=
          dvbdemux->frontend_list.prev=
            &dvbdemux->frontend_list;
        for (i=0; i<DMX_TS_PES_OTHER; i++) {
                dvbdemux->pesfilter[i]=NULL;
                dvbdemux->pids[i]=0xffff;
	}
        dvbdemux->playing=dvbdemux->recording=0;
        memset(dvbdemux->pid2feed, 0, 0x2000*sizeof(dvb_demux_feed_t *));
        dvbdemux->tsbufp=0;

        dmx->frontend=0;
        dmx->reg_list.next=dmx->reg_list.prev=&dmx->reg_list;
        dmx->priv=(void *) dvbdemux;
        //dmx->users=0;                  // reset in dmx_register_demux() 
        dmx->open=dvbdmx_open;
        dmx->close=dvbdmx_close;
        dmx->write=dvbdmx_write;
        dmx->allocate_ts_feed=dvbdmx_allocate_ts_feed;
        dmx->release_ts_feed=dvbdmx_release_ts_feed;
        dmx->allocate_pes_feed=dvbdmx_allocate_pes_feed;
        dmx->release_pes_feed=dvbdmx_release_pes_feed;
        dmx->allocate_section_feed=dvbdmx_allocate_section_feed;
        dmx->release_section_feed=dvbdmx_release_section_feed;

        dmx->descramble_mac_address=NULL;
        dmx->descramble_section_payload=NULL;
        
        dmx->add_frontend=dvbdmx_add_frontend;
        dmx->remove_frontend=dvbdmx_remove_frontend;
        dmx->get_frontends=dvbdmx_get_frontends;
        dmx->connect_frontend=dvbdmx_connect_frontend;
        dmx->disconnect_frontend=dvbdmx_disconnect_frontend;
        dmx->get_pes_pids=dvbdmx_get_pes_pids;

        if (dmx_register_demux(dmx)<0) 
                return -1;

        sema_init(&dvbdemux->mutex, 1);
        return 0;
}

int 
DvbDmxRelease(dvb_demux_t *dvbdemux)
{
        dmx_demux_t *dmx=&dvbdemux->dmx;

        dmx_unregister_demux(dmx);
	if (dvbdemux->filter)
                vfree(dvbdemux->filter);
	if (dvbdemux->feed)
                vfree(dvbdemux->feed);
        return 0;
}

LinuxTV legacy CVS <linuxtv.org/cvs>