/* * dvb_demux.c - DVB kernel demux API * * Copyright (C) 2000-2001 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 #include #include #include #include "dvb_demux.h" /* allocate no buffers, firmware buffers good enough (and somewhere there is a memory leak ...) */ #define NOBUFS 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; } static u32 dvb_crc_table[256] = { 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4}; u32 dvb_crc32(u8 *data, int len) { int i; u32 crc = 0xffffffff; for (i=0; i> 24) ^ *data++) & 0xff]; return crc; } void dvb_set_crc32(u8 *data, int length) { u32 crc; crc=dvb_crc32(data,length); data[length] = (crc>>24)&0xff; data[length+1] = (crc>>16)&0xff; data[length+2] = (crc>>8)&0xff; data[length+3] = (crc)&0xff; } /****************************************************************************** * 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; ifilter_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 inline 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); //if ((dvbdmxfeed=dvbdmx->pid2feed[0x1fff])) // 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=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; ifilternum; 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; ifeednum; 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; dvbdmx->pids[pes_type]=dvbdmxfeed->pid; } return 0; } static int dmx_pid_set(__u16 pid, dvb_demux_feed_t *dvbdmxfeed) { dvb_demux_t *dvbdmx=dvbdmxfeed->demux; dvb_demux_feed_t **pid2feed=dvbdmx->pid2feed; if (pid>0x1fff) return -EINVAL; if (dvbdmxfeed->pid!=0xffff) { if (dvbdmxfeed->pid<=0x1fff) pid2feed[dvbdmxfeed->pid]=0; dvbdmxfeed->pid=0xffff; } if (pid2feed[pid]) return -EBUSY; pid2feed[pid]=dvbdmxfeed; dvbdmxfeed->pid=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; #if 0 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; #else int ret; ret=dmx_pid_set(pid, dvbdmxfeed); if (ret<0) return ret; #endif 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) { #ifdef NOBUFS dvbdmxfeed->buffer=0; #else dvbdmxfeed->buffer=vmalloc(dvbdmxfeed->buffer_size); if (!dvbdmxfeed->buffer) return -ENOMEM; #endif } 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->statestop_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; 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; #ifdef NOBUFS dvbdmxfeed->buffer=0; #else dvbdmxfeed->buffer=vmalloc(dvbdmxfeed->buffer_size); if (!dvbdmxfeed->buffer) return -ENOMEM; #endif 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; ifilternum; i++) { dvbdemux->filter[i].state=DMX_STATE_FREE; dvbdemux->filter[i].index=i; } for (i=0; ifeednum; i++) dvbdemux->feed[i].state=DMX_STATE_FREE; dvbdemux->frontend_list.next= dvbdemux->frontend_list.prev= &dvbdemux->frontend_list; for (i=0; ipesfilter[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; }