diff options
Diffstat (limited to 'drivers/media/dvb/dvb-core/dvb_demux.c')
-rw-r--r-- | drivers/media/dvb/dvb-core/dvb_demux.c | 1318 |
1 files changed, 0 insertions, 1318 deletions
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c deleted file mode 100644 index 17cb81fd194..00000000000 --- a/drivers/media/dvb/dvb-core/dvb_demux.c +++ /dev/null @@ -1,1318 +0,0 @@ -/* - * 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/sched.h> -#include <linux/spinlock.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> -#include <linux/module.h> -#include <linux/poll.h> -#include <linux/string.h> -#include <linux/crc32.h> -#include <asm/uaccess.h> -#include <asm/div64.h> - -#include "dvb_demux.h" - -#define NOBUFS -/* -** #define DVB_DEMUX_SECTION_LOSS_LOG to monitor payload loss in the syslog -*/ -// #define DVB_DEMUX_SECTION_LOSS_LOG - -static int dvb_demux_tscheck; -module_param(dvb_demux_tscheck, int, 0644); -MODULE_PARM_DESC(dvb_demux_tscheck, - "enable transport stream continuity and TEI check"); - -static int dvb_demux_speedcheck; -module_param(dvb_demux_speedcheck, int, 0644); -MODULE_PARM_DESC(dvb_demux_speedcheck, - "enable transport stream speed check"); - -static int dvb_demux_feed_err_pkts = 1; -module_param(dvb_demux_feed_err_pkts, int, 0644); -MODULE_PARM_DESC(dvb_demux_feed_err_pkts, - "when set to 0, drop packets with the TEI bit set (1 by default)"); - -#define dprintk_tscheck(x...) do { \ - if (dvb_demux_tscheck && printk_ratelimit()) \ - printk(x); \ - } while (0) - -/****************************************************************************** - * 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 u8 payload(const u8 *tsp) -{ - if (!(tsp[3] & 0x10)) // no payload? - return 0; - - if (tsp[3] & 0x20) { // adaptation field? - if (tsp[4] > 183) // corrupted data? - return 0; - else - return 184 - 1 - tsp[4]; - } - - return 184; -} - -static u32 dvb_dmx_crc32(struct dvb_demux_feed *f, const u8 *src, size_t len) -{ - return (f->feed.sec.crc_val = crc32_be(f->feed.sec.crc_val, src, len)); -} - -static void dvb_dmx_memcopy(struct dvb_demux_feed *f, u8 *d, const u8 *s, - size_t len) -{ - memcpy(d, s, len); -} - -/****************************************************************************** - * Software filter functions - ******************************************************************************/ - -static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed, - const u8 *buf) -{ - int count = payload(buf); - int p; - //int ccok; - //u8 cc; - - if (count == 0) - return -1; - - p = 188 - count; - - /* - cc = buf[3] & 0x0f; - ccok = ((feed->cc + 1) & 0x0f) == cc; - feed->cc = cc; - if (!ccok) - printk("missed packet!\n"); - */ - - if (buf[1] & 0x40) // PUSI ? - feed->peslen = 0xfffa; - - feed->peslen += count; - - return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts, DMX_OK); -} - -static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed, - struct dvb_demux_filter *f) -{ - u8 neq = 0; - int i; - - for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) { - u8 xor = f->filter.filter_value[i] ^ feed->feed.sec.secbuf[i]; - - if (f->maskandmode[i] & xor) - return 0; - - neq |= f->maskandnotmode[i] & xor; - } - - if (f->doneq && !neq) - return 0; - - return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen, - NULL, 0, &f->filter, DMX_OK); -} - -static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct dvb_demux_filter *f = feed->filter; - struct dmx_section_feed *sec = &feed->feed.sec; - int section_syntax_indicator; - - if (!sec->is_filtering) - return 0; - - if (!f) - return 0; - - if (sec->check_crc) { - section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0); - if (section_syntax_indicator && - demux->check_crc32(feed, sec->secbuf, sec->seclen)) - return -1; - } - - do { - if (dvb_dmx_swfilter_sectionfilter(feed, f) < 0) - return -1; - } while ((f = f->next) && sec->is_filtering); - - sec->seclen = 0; - - return 0; -} - -static void dvb_dmx_swfilter_section_new(struct dvb_demux_feed *feed) -{ - struct dmx_section_feed *sec = &feed->feed.sec; - -#ifdef DVB_DEMUX_SECTION_LOSS_LOG - if (sec->secbufp < sec->tsfeedp) { - int i, n = sec->tsfeedp - sec->secbufp; - - /* - * Section padding is done with 0xff bytes entirely. - * Due to speed reasons, we won't check all of them - * but just first and last. - */ - if (sec->secbuf[0] != 0xff || sec->secbuf[n - 1] != 0xff) { - printk("dvb_demux.c section ts padding loss: %d/%d\n", - n, sec->tsfeedp); - printk("dvb_demux.c pad data:"); - for (i = 0; i < n; i++) - printk(" %02x", sec->secbuf[i]); - printk("\n"); - } - } -#endif - - sec->tsfeedp = sec->secbufp = sec->seclen = 0; - sec->secbuf = sec->secbuf_base; -} - -/* - * Losless Section Demux 1.4.1 by Emard - * Valsecchi Patrick: - * - middle of section A (no PUSI) - * - end of section A and start of section B - * (with PUSI pointing to the start of the second section) - * - * In this case, without feed->pusi_seen you'll receive a garbage section - * consisting of the end of section A. Basically because tsfeedp - * is incemented and the use=0 condition is not raised - * when the second packet arrives. - * - * Fix: - * when demux is started, let feed->pusi_seen = 0 to - * prevent initial feeding of garbage from the end of - * previous section. When you for the first time see PUSI=1 - * then set feed->pusi_seen = 1 - */ -static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed, - const u8 *buf, u8 len) -{ - struct dvb_demux *demux = feed->demux; - struct dmx_section_feed *sec = &feed->feed.sec; - u16 limit, seclen, n; - - if (sec->tsfeedp >= DMX_MAX_SECFEED_SIZE) - return 0; - - if (sec->tsfeedp + len > DMX_MAX_SECFEED_SIZE) { -#ifdef DVB_DEMUX_SECTION_LOSS_LOG - printk("dvb_demux.c section buffer full loss: %d/%d\n", - sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE, - DMX_MAX_SECFEED_SIZE); -#endif - len = DMX_MAX_SECFEED_SIZE - sec->tsfeedp; - } - - if (len <= 0) - return 0; - - demux->memcopy(feed, sec->secbuf_base + sec->tsfeedp, buf, len); - sec->tsfeedp += len; - - /* - * Dump all the sections we can find in the data (Emard) - */ - limit = sec->tsfeedp; - if (limit > DMX_MAX_SECFEED_SIZE) - return -1; /* internal error should never happen */ - - /* to be sure always set secbuf */ - sec->secbuf = sec->secbuf_base + sec->secbufp; - - for (n = 0; sec->secbufp + 2 < limit; n++) { - seclen = section_length(sec->secbuf); - if (seclen <= 0 || seclen > DMX_MAX_SECTION_SIZE - || seclen + sec->secbufp > limit) - return 0; - sec->seclen = seclen; - sec->crc_val = ~0; - /* dump [secbuf .. secbuf+seclen) */ - if (feed->pusi_seen) - dvb_dmx_swfilter_section_feed(feed); -#ifdef DVB_DEMUX_SECTION_LOSS_LOG - else - printk("dvb_demux.c pusi not seen, discarding section data\n"); -#endif - sec->secbufp += seclen; /* secbufp and secbuf moving together is */ - sec->secbuf += seclen; /* redundant but saves pointer arithmetic */ - } - - return 0; -} - -static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed, - const u8 *buf) -{ - u8 p, count; - int ccok, dc_i = 0; - u8 cc; - - count = payload(buf); - - if (count == 0) /* count == 0 if no payload or out of range */ - return -1; - - p = 188 - count; /* payload start */ - - cc = buf[3] & 0x0f; - ccok = ((feed->cc + 1) & 0x0f) == cc; - feed->cc = cc; - - if (buf[3] & 0x20) { - /* adaption field present, check for discontinuity_indicator */ - if ((buf[4] > 0) && (buf[5] & 0x80)) - dc_i = 1; - } - - if (!ccok || dc_i) { -#ifdef DVB_DEMUX_SECTION_LOSS_LOG - printk("dvb_demux.c discontinuity detected %d bytes lost\n", - count); - /* - * those bytes under sume circumstances will again be reported - * in the following dvb_dmx_swfilter_section_new - */ -#endif - /* - * Discontinuity detected. Reset pusi_seen = 0 to - * stop feeding of suspicious data until next PUSI=1 arrives - */ - feed->pusi_seen = 0; - dvb_dmx_swfilter_section_new(feed); - } - - if (buf[1] & 0x40) { - /* PUSI=1 (is set), section boundary is here */ - if (count > 1 && buf[p] < count) { - const u8 *before = &buf[p + 1]; - u8 before_len = buf[p]; - const u8 *after = &before[before_len]; - u8 after_len = count - 1 - before_len; - - dvb_dmx_swfilter_section_copy_dump(feed, before, - before_len); - /* before start of new section, set pusi_seen = 1 */ - feed->pusi_seen = 1; - dvb_dmx_swfilter_section_new(feed); - dvb_dmx_swfilter_section_copy_dump(feed, after, - after_len); - } -#ifdef DVB_DEMUX_SECTION_LOSS_LOG - else if (count > 0) - printk("dvb_demux.c PUSI=1 but %d bytes lost\n", count); -#endif - } else { - /* PUSI=0 (is not set), no section boundary */ - dvb_dmx_swfilter_section_copy_dump(feed, &buf[p], count); - } - - return 0; -} - -static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed, - const u8 *buf) -{ - switch (feed->type) { - case DMX_TYPE_TS: - if (!feed->feed.ts.is_filtering) - break; - if (feed->ts_type & TS_PACKET) { - if (feed->ts_type & TS_PAYLOAD_ONLY) - dvb_dmx_swfilter_payload(feed, buf); - else - feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, - DMX_OK); - } - if (feed->ts_type & TS_DECODER) - if (feed->demux->write_to_decoder) - feed->demux->write_to_decoder(feed, buf, 188); - break; - - case DMX_TYPE_SEC: - if (!feed->feed.sec.is_filtering) - break; - if (dvb_dmx_swfilter_section_packet(feed, buf) < 0) - feed->feed.sec.seclen = feed->feed.sec.secbufp = 0; - break; - - default: - break; - } -} - -#define DVR_FEED(f) \ - (((f)->type == DMX_TYPE_TS) && \ - ((f)->feed.ts.is_filtering) && \ - (((f)->ts_type & (TS_PACKET | TS_DEMUX)) == TS_PACKET)) - -static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) -{ - struct dvb_demux_feed *feed; - u16 pid = ts_pid(buf); - int dvr_done = 0; - - if (dvb_demux_speedcheck) { - struct timespec cur_time, delta_time; - u64 speed_bytes, speed_timedelta; - - demux->speed_pkts_cnt++; - - /* show speed every SPEED_PKTS_INTERVAL packets */ - if (!(demux->speed_pkts_cnt % SPEED_PKTS_INTERVAL)) { - cur_time = current_kernel_time(); - - if (demux->speed_last_time.tv_sec != 0 && - demux->speed_last_time.tv_nsec != 0) { - delta_time = timespec_sub(cur_time, - demux->speed_last_time); - speed_bytes = (u64)demux->speed_pkts_cnt - * 188 * 8; - /* convert to 1024 basis */ - speed_bytes = 1000 * div64_u64(speed_bytes, - 1024); - speed_timedelta = - (u64)timespec_to_ns(&delta_time); - speed_timedelta = div64_u64(speed_timedelta, - 1000000); /* nsec -> usec */ - printk(KERN_INFO "TS speed %llu Kbits/sec \n", - div64_u64(speed_bytes, - speed_timedelta)); - }; - - demux->speed_last_time = cur_time; - demux->speed_pkts_cnt = 0; - }; - }; - - if (buf[1] & 0x80) { - dprintk_tscheck("TEI detected. " - "PID=0x%x data1=0x%x\n", - pid, buf[1]); - /* data in this packet cant be trusted - drop it unless - * module option dvb_demux_feed_err_pkts is set */ - if (!dvb_demux_feed_err_pkts) - return; - } else /* if TEI bit is set, pid may be wrong- skip pkt counter */ - if (demux->cnt_storage && dvb_demux_tscheck) { - /* check pkt counter */ - if (pid < MAX_PID) { - if ((buf[3] & 0xf) != demux->cnt_storage[pid]) - dprintk_tscheck("TS packet counter mismatch. " - "PID=0x%x expected 0x%x " - "got 0x%x\n", - pid, demux->cnt_storage[pid], - buf[3] & 0xf); - - demux->cnt_storage[pid] = ((buf[3] & 0xf) + 1)&0xf; - }; - /* end check */ - }; - - list_for_each_entry(feed, &demux->feed_list, list_head) { - if ((feed->pid != pid) && (feed->pid != 0x2000)) - continue; - - /* copy each packet only once to the dvr device, even - * if a PID is in multiple filters (e.g. video + PCR) */ - if ((DVR_FEED(feed)) && (dvr_done++)) - continue; - - if (feed->pid == pid) - dvb_dmx_swfilter_packet_type(feed, buf); - else if (feed->pid == 0x2000) - feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK); - } -} - -void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf, - size_t count) -{ - spin_lock(&demux->lock); - - while (count--) { - if (buf[0] == 0x47) - dvb_dmx_swfilter_packet(demux, buf); - buf += 188; - } - - spin_unlock(&demux->lock); -} - -EXPORT_SYMBOL(dvb_dmx_swfilter_packets); - -static inline int find_next_packet(const u8 *buf, int pos, size_t count, - const int pktsize) -{ - int start = pos, lost; - - while (pos < count) { - if (buf[pos] == 0x47 || - (pktsize == 204 && buf[pos] == 0xB8)) - break; - pos++; - } - - lost = pos - start; - if (lost) { - /* This garbage is part of a valid packet? */ - int backtrack = pos - pktsize; - if (backtrack >= 0 && (buf[backtrack] == 0x47 || - (pktsize == 204 && buf[backtrack] == 0xB8))) - return backtrack; - } - - return pos; -} - -/* Filter all pktsize= 188 or 204 sized packets and skip garbage. */ -static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, - size_t count, const int pktsize) -{ - int p = 0, i, j; - const u8 *q; - - spin_lock(&demux->lock); - - if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */ - i = demux->tsbufp; - j = pktsize - i; - if (count < j) { - memcpy(&demux->tsbuf[i], buf, count); - demux->tsbufp += count; - goto bailout; - } - memcpy(&demux->tsbuf[i], buf, j); - if (demux->tsbuf[0] == 0x47) /* double check */ - dvb_dmx_swfilter_packet(demux, demux->tsbuf); - demux->tsbufp = 0; - p += j; - } - - while (1) { - p = find_next_packet(buf, p, count, pktsize); - if (p >= count) - break; - if (count - p < pktsize) - break; - - q = &buf[p]; - - if (pktsize == 204 && (*q == 0xB8)) { - memcpy(demux->tsbuf, q, 188); - demux->tsbuf[0] = 0x47; - q = demux->tsbuf; - } - dvb_dmx_swfilter_packet(demux, q); - p += pktsize; - } - - i = count - p; - if (i) { - memcpy(demux->tsbuf, &buf[p], i); - demux->tsbufp = i; - if (pktsize == 204 && demux->tsbuf[0] == 0xB8) - demux->tsbuf[0] = 0x47; - } - -bailout: - spin_unlock(&demux->lock); -} - -void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count) -{ - _dvb_dmx_swfilter(demux, buf, count, 188); -} -EXPORT_SYMBOL(dvb_dmx_swfilter); - -void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count) -{ - _dvb_dmx_swfilter(demux, buf, count, 204); -} -EXPORT_SYMBOL(dvb_dmx_swfilter_204); - -void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count) -{ - spin_lock(&demux->lock); - - demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts, DMX_OK); - - spin_unlock(&demux->lock); -} -EXPORT_SYMBOL(dvb_dmx_swfilter_raw); - -static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux) -{ - int i; - - for (i = 0; i < demux->filternum; i++) - if (demux->filter[i].state == DMX_STATE_FREE) - break; - - if (i == demux->filternum) - return NULL; - - demux->filter[i].state = DMX_STATE_ALLOCATED; - - return &demux->filter[i]; -} - -static struct dvb_demux_feed *dvb_dmx_feed_alloc(struct dvb_demux *demux) -{ - int i; - - for (i = 0; i < demux->feednum; i++) - if (demux->feed[i].state == DMX_STATE_FREE) - break; - - if (i == demux->feednum) - return NULL; - - demux->feed[i].state = DMX_STATE_ALLOCATED; - - return &demux->feed[i]; -} - -static int dvb_demux_feed_find(struct dvb_demux_feed *feed) -{ - struct dvb_demux_feed *entry; - - list_for_each_entry(entry, &feed->demux->feed_list, list_head) - if (entry == feed) - return 1; - - return 0; -} - -static void dvb_demux_feed_add(struct dvb_demux_feed *feed) -{ - spin_lock_irq(&feed->demux->lock); - if (dvb_demux_feed_find(feed)) { - printk(KERN_ERR "%s: feed already in list (type=%x state=%x pid=%x)\n", - __func__, feed->type, feed->state, feed->pid); - goto out; - } - - list_add(&feed->list_head, &feed->demux->feed_list); -out: - spin_unlock_irq(&feed->demux->lock); -} - -static void dvb_demux_feed_del(struct dvb_demux_feed *feed) -{ - spin_lock_irq(&feed->demux->lock); - if (!(dvb_demux_feed_find(feed))) { - printk(KERN_ERR "%s: feed not in list (type=%x state=%x pid=%x)\n", - __func__, feed->type, feed->state, feed->pid); - goto out; - } - - list_del(&feed->list_head); -out: - spin_unlock_irq(&feed->demux->lock); -} - -static int dmx_ts_feed_set(struct dmx_ts_feed *ts_feed, u16 pid, int ts_type, - enum dmx_ts_pes pes_type, - size_t circular_buffer_size, struct timespec timeout) -{ - struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; - struct dvb_demux *demux = feed->demux; - - if (pid > DMX_MAX_PID) - return -EINVAL; - - if (mutex_lock_interruptible(&demux->mutex)) - return -ERESTARTSYS; - - if (ts_type & TS_DECODER) { - if (pes_type >= DMX_TS_PES_OTHER) { - mutex_unlock(&demux->mutex); - return -EINVAL; - } - - if (demux->pesfilter[pes_type] && - demux->pesfilter[pes_type] != feed) { - mutex_unlock(&demux->mutex); - return -EINVAL; - } - - demux->pesfilter[pes_type] = feed; - demux->pids[pes_type] = pid; - } - - dvb_demux_feed_add(feed); - - feed->pid = pid; - feed->buffer_size = circular_buffer_size; - feed->timeout = timeout; - feed->ts_type = ts_type; - feed->pes_type = pes_type; - - if (feed->buffer_size) { -#ifdef NOBUFS - feed->buffer = NULL; -#else - feed->buffer = vmalloc(feed->buffer_size); - if (!feed->buffer) { - mutex_unlock(&demux->mutex); - return -ENOMEM; - } -#endif - } - - feed->state = DMX_STATE_READY; - mutex_unlock(&demux->mutex); - - return 0; -} - -static int dmx_ts_feed_start_filtering(struct dmx_ts_feed *ts_feed) -{ - struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; - struct dvb_demux *demux = feed->demux; - int ret; - - if (mutex_lock_interruptible(&demux->mutex)) - return -ERESTARTSYS; - - if (feed->state != DMX_STATE_READY || feed->type != DMX_TYPE_TS) { - mutex_unlock(&demux->mutex); - return -EINVAL; - } - - if (!demux->start_feed) { - mutex_unlock(&demux->mutex); - return -ENODEV; - } - - if ((ret = demux->start_feed(feed)) < 0) { - mutex_unlock(&demux->mutex); - return ret; - } - - spin_lock_irq(&demux->lock); - ts_feed->is_filtering = 1; - feed->state = DMX_STATE_GO; - spin_unlock_irq(&demux->lock); - mutex_unlock(&demux->mutex); - - return 0; -} - -static int dmx_ts_feed_stop_filtering(struct dmx_ts_feed *ts_feed) -{ - struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; - struct dvb_demux *demux = feed->demux; - int ret; - - mutex_lock(&demux->mutex); - - if (feed->state < DMX_STATE_GO) { - mutex_unlock(&demux->mutex); - return -EINVAL; - } - - if (!demux->stop_feed) { - mutex_unlock(&demux->mutex); - return -ENODEV; - } - - ret = demux->stop_feed(feed); - - spin_lock_irq(&demux->lock); - ts_feed->is_filtering = 0; - feed->state = DMX_STATE_ALLOCATED; - spin_unlock_irq(&demux->lock); - mutex_unlock(&demux->mutex); - - return ret; -} - -static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx, - struct dmx_ts_feed **ts_feed, - dmx_ts_cb callback) -{ - struct dvb_demux *demux = (struct dvb_demux *)dmx; - struct dvb_demux_feed *feed; - - if (mutex_lock_interruptible(&demux->mutex)) - return -ERESTARTSYS; - - if (!(feed = dvb_dmx_feed_alloc(demux))) { - mutex_unlock(&demux->mutex); - return -EBUSY; - } - - feed->type = DMX_TYPE_TS; - feed->cb.ts = callback; - feed->demux = demux; - feed->pid = 0xffff; - feed->peslen = 0xfffa; - feed->buffer = NULL; - - (*ts_feed) = &feed->feed.ts; - (*ts_feed)->parent = dmx; - (*ts_feed)->priv = NULL; - (*ts_feed)->is_filtering = 0; - (*ts_feed)->start_filtering = dmx_ts_feed_start_filtering; - (*ts_feed)->stop_filtering = dmx_ts_feed_stop_filtering; - (*ts_feed)->set = dmx_ts_feed_set; - - if (!(feed->filter = dvb_dmx_filter_alloc(demux))) { - feed->state = DMX_STATE_FREE; - mutex_unlock(&demux->mutex); - return -EBUSY; - } - - feed->filter->type = DMX_TYPE_TS; - feed->filter->feed = feed; - feed->filter->state = DMX_STATE_READY; - - mutex_unlock(&demux->mutex); - - return 0; -} - -static int dvbdmx_release_ts_feed(struct dmx_demux *dmx, - struct dmx_ts_feed *ts_feed) -{ - struct dvb_demux *demux = (struct dvb_demux *)dmx; - struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; - - mutex_lock(&demux->mutex); - - if (feed->state == DMX_STATE_FREE) { - mutex_unlock(&demux->mutex); - return -EINVAL; - } -#ifndef NOBUFS - vfree(feed->buffer); - feed->buffer = NULL; -#endif - - feed->state = DMX_STATE_FREE; - feed->filter->state = DMX_STATE_FREE; - - dvb_demux_feed_del(feed); - - feed->pid = 0xffff; - - if (feed->ts_type & TS_DECODER && feed->pes_type < DMX_TS_PES_OTHER) - demux->pesfilter[feed->pes_type] = NULL; - - mutex_unlock(&demux->mutex); - return 0; -} - -/****************************************************************************** - * dmx_section_feed API calls - ******************************************************************************/ - -static int dmx_section_feed_allocate_filter(struct dmx_section_feed *feed, - struct dmx_section_filter **filter) -{ - struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; - struct dvb_demux *dvbdemux = dvbdmxfeed->demux; - struct dvb_demux_filter *dvbdmxfilter; - - if (mutex_lock_interruptible(&dvbdemux->mutex)) - return -ERESTARTSYS; - - dvbdmxfilter = dvb_dmx_filter_alloc(dvbdemux); - if (!dvbdmxfilter) { - mutex_unlock(&dvbdemux->mutex); - return -EBUSY; - } - - spin_lock_irq(&dvbdemux->lock); - *filter = &dvbdmxfilter->filter; - (*filter)->parent = feed; - (*filter)->priv = NULL; - dvbdmxfilter->feed = dvbdmxfeed; - dvbdmxfilter->type = DMX_TYPE_SEC; - dvbdmxfilter->state = DMX_STATE_READY; - dvbdmxfilter->next = dvbdmxfeed->filter; - dvbdmxfeed->filter = dvbdmxfilter; - spin_unlock_irq(&dvbdemux->lock); - - mutex_unlock(&dvbdemux->mutex); - return 0; -} - -static int dmx_section_feed_set(struct dmx_section_feed *feed, - u16 pid, size_t circular_buffer_size, - int check_crc) -{ - struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - - if (pid > 0x1fff) - return -EINVAL; - - if (mutex_lock_interruptible(&dvbdmx->mutex)) - return -ERESTARTSYS; - - dvb_demux_feed_add(dvbdmxfeed); - - dvbdmxfeed->pid = pid; - dvbdmxfeed->buffer_size = circular_buffer_size; - dvbdmxfeed->feed.sec.check_crc = check_crc; - -#ifdef NOBUFS - dvbdmxfeed->buffer = NULL; -#else - dvbdmxfeed->buffer = vmalloc(dvbdmxfeed->buffer_size); - if (!dvbdmxfeed->buffer) { - mutex_unlock(&dvbdmx->mutex); - return -ENOMEM; - } -#endif - - dvbdmxfeed->state = DMX_STATE_READY; - mutex_unlock(&dvbdmx->mutex); - return 0; -} - -static void prepare_secfilters(struct dvb_demux_feed *dvbdmxfeed) -{ - int i; - struct dvb_demux_filter *f; - struct dmx_section_filter *sf; - u8 mask, mode, doneq; - - if (!(f = dvbdmxfeed->filter)) - return; - do { - sf = &f->filter; - doneq = 0; - for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) { - mode = sf->filter_mode[i]; - mask = sf->filter_mask[i]; - f->maskandmode[i] = mask & mode; - doneq |= f->maskandnotmode[i] = mask & ~mode; - } - f->doneq = doneq ? 1 : 0; - } while ((f = f->next)); -} - -static int dmx_section_feed_start_filtering(struct dmx_section_feed *feed) -{ - struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - int ret; - - if (mutex_lock_interruptible(&dvbdmx->mutex)) - return -ERESTARTSYS; - - if (feed->is_filtering) { - mutex_unlock(&dvbdmx->mutex); - return -EBUSY; - } - - if (!dvbdmxfeed->filter) { - mutex_unlock(&dvbdmx->mutex); - return -EINVAL; - } - - dvbdmxfeed->feed.sec.tsfeedp = 0; - dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base; - dvbdmxfeed->feed.sec.secbufp = 0; - dvbdmxfeed->feed.sec.seclen = 0; - - if (!dvbdmx->start_feed) { - mutex_unlock(&dvbdmx->mutex); - return -ENODEV; - } - - prepare_secfilters(dvbdmxfeed); - - if ((ret = dvbdmx->start_feed(dvbdmxfeed)) < 0) { - mutex_unlock(&dvbdmx->mutex); - return ret; - } - - spin_lock_irq(&dvbdmx->lock); - feed->is_filtering = 1; - dvbdmxfeed->state = DMX_STATE_GO; - spin_unlock_irq(&dvbdmx->lock); - - mutex_unlock(&dvbdmx->mutex); - return 0; -} - -static int dmx_section_feed_stop_filtering(struct dmx_section_feed *feed) -{ - struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - int ret; - - mutex_lock(&dvbdmx->mutex); - - if (!dvbdmx->stop_feed) { - mutex_unlock(&dvbdmx->mutex); - return -ENODEV; - } - - ret = dvbdmx->stop_feed(dvbdmxfeed); - - spin_lock_irq(&dvbdmx->lock); - dvbdmxfeed->state = DMX_STATE_READY; - feed->is_filtering = 0; - spin_unlock_irq(&dvbdmx->lock); - - mutex_unlock(&dvbdmx->mutex); - return ret; -} - -static int dmx_section_feed_release_filter(struct dmx_section_feed *feed, - struct dmx_section_filter *filter) -{ - struct dvb_demux_filter *dvbdmxfilter = (struct dvb_demux_filter *)filter, *f; - struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; - struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - - mutex_lock(&dvbdmx->mutex); - - if (dvbdmxfilter->feed != dvbdmxfeed) { - mutex_unlock(&dvbdmx->mutex); - return -EINVAL; - } - - if (feed->is_filtering) - feed->stop_filtering(feed); - - spin_lock_irq(&dvbdmx->lock); - 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; - spin_unlock_irq(&dvbdmx->lock); - mutex_unlock(&dvbdmx->mutex); - return 0; -} - -static int dvbdmx_allocate_section_feed(struct dmx_demux *demux, - struct dmx_section_feed **feed, - dmx_section_cb callback) -{ - struct dvb_demux *dvbdmx = (struct dvb_demux *)demux; - struct dvb_demux_feed *dvbdmxfeed; - - if (mutex_lock_interruptible(&dvbdmx->mutex)) - return -ERESTARTSYS; - - if (!(dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx))) { - mutex_unlock(&dvbdmx->mutex); - return -EBUSY; - } - - dvbdmxfeed->type = DMX_TYPE_SEC; - dvbdmxfeed->cb.sec = callback; - dvbdmxfeed->demux = dvbdmx; - dvbdmxfeed->pid = 0xffff; - dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base; - dvbdmxfeed->feed.sec.secbufp = dvbdmxfeed->feed.sec.seclen = 0; - dvbdmxfeed->feed.sec.tsfeedp = 0; - dvbdmxfeed->filter = NULL; - dvbdmxfeed->buffer = NULL; - - (*feed) = &dvbdmxfeed->feed.sec; - (*feed)->is_filtering = 0; - (*feed)->parent = demux; - (*feed)->priv = NULL; - - (*feed)->set = dmx_section_feed_set; - (*feed)->allocate_filter = dmx_section_feed_allocate_filter; - (*feed)->start_filtering = dmx_section_feed_start_filtering; - (*feed)->stop_filtering = dmx_section_feed_stop_filtering; - (*feed)->release_filter = dmx_section_feed_release_filter; - - mutex_unlock(&dvbdmx->mutex); - return 0; -} - -static int dvbdmx_release_section_feed(struct dmx_demux *demux, - struct dmx_section_feed *feed) -{ - struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; - struct dvb_demux *dvbdmx = (struct dvb_demux *)demux; - - mutex_lock(&dvbdmx->mutex); - - if (dvbdmxfeed->state == DMX_STATE_FREE) { - mutex_unlock(&dvbdmx->mutex); - return -EINVAL; - } -#ifndef NOBUFS - vfree(dvbdmxfeed->buffer); - dvbdmxfeed->buffer = NULL; -#endif - dvbdmxfeed->state = DMX_STATE_FREE; - - dvb_demux_feed_del(dvbdmxfeed); - - dvbdmxfeed->pid = 0xffff; - - mutex_unlock(&dvbdmx->mutex); - return 0; -} - -/****************************************************************************** - * dvb_demux kernel data API calls - ******************************************************************************/ - -static int dvbdmx_open(struct dmx_demux *demux) -{ - struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; - - if (dvbdemux->users >= MAX_DVB_DEMUX_USERS) - return -EUSERS; - - dvbdemux->users++; - return 0; -} - -static int dvbdmx_close(struct dmx_demux *demux) -{ - struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; - - if (dvbdemux->users == 0) - return -ENODEV; - - dvbdemux->users--; - //FIXME: release any unneeded resources if users==0 - return 0; -} - -static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t count) -{ - struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; - void *p; - - if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE)) - return -EINVAL; - - p = memdup_user(buf, count); - if (IS_ERR(p)) - return PTR_ERR(p); - if (mutex_lock_interruptible(&dvbdemux->mutex)) { - kfree(p); - return -ERESTARTSYS; - } - dvb_dmx_swfilter(dvbdemux, p, count); - kfree(p); - mutex_unlock(&dvbdemux->mutex); - - if (signal_pending(current)) - return -EINTR; - return count; -} - -static int dvbdmx_add_frontend(struct dmx_demux *demux, - struct dmx_frontend *frontend) -{ - struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; - struct list_head *head = &dvbdemux->frontend_list; - - list_add(&(frontend->connectivity_list), head); - - return 0; -} - -static int dvbdmx_remove_frontend(struct dmx_demux *demux, - struct dmx_frontend *frontend) -{ - struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; - struct list_head *pos, *n, *head = &dvbdemux->frontend_list; - - list_for_each_safe(pos, n, head) { - if (DMX_FE_ENTRY(pos) == frontend) { - list_del(pos); - return 0; - } - } - - return -ENODEV; -} - -static struct list_head *dvbdmx_get_frontends(struct dmx_demux *demux) -{ - struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; - - if (list_empty(&dvbdemux->frontend_list)) - return NULL; - - return &dvbdemux->frontend_list; -} - -static int dvbdmx_connect_frontend(struct dmx_demux *demux, - struct dmx_frontend *frontend) -{ - struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; - - if (demux->frontend) - return -EINVAL; - - mutex_lock(&dvbdemux->mutex); - - demux->frontend = frontend; - mutex_unlock(&dvbdemux->mutex); - return 0; -} - -static int dvbdmx_disconnect_frontend(struct dmx_demux *demux) -{ - struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; - - mutex_lock(&dvbdemux->mutex); - - demux->frontend = NULL; - mutex_unlock(&dvbdemux->mutex); - return 0; -} - -static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 * pids) -{ - struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; - - memcpy(pids, dvbdemux->pids, 5 * sizeof(u16)); - return 0; -} - -int dvb_dmx_init(struct dvb_demux *dvbdemux) -{ - int i; - struct dmx_demux *dmx = &dvbdemux->dmx; - - dvbdemux->cnt_storage = NULL; - dvbdemux->users = 0; - dvbdemux->filter = vmalloc(dvbdemux->filternum * sizeof(struct dvb_demux_filter)); - - if (!dvbdemux->filter) - return -ENOMEM; - - dvbdemux->feed = vmalloc(dvbdemux->feednum * sizeof(struct dvb_demux_feed)); - if (!dvbdemux->feed) { - vfree(dvbdemux->filter); - dvbdemux->filter = NULL; - 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->feed[i].index = i; - } - - dvbdemux->cnt_storage = vmalloc(MAX_PID + 1); - if (!dvbdemux->cnt_storage) - printk(KERN_WARNING "Couldn't allocate memory for TS/TEI check. Disabling it\n"); - - INIT_LIST_HEAD(&dvbdemux->frontend_list); - - for (i = 0; i < DMX_TS_PES_OTHER; i++) { - dvbdemux->pesfilter[i] = NULL; - dvbdemux->pids[i] = 0xffff; - } - - INIT_LIST_HEAD(&dvbdemux->feed_list); - - dvbdemux->playing = 0; - dvbdemux->recording = 0; - dvbdemux->tsbufp = 0; - - if (!dvbdemux->check_crc32) - dvbdemux->check_crc32 = dvb_dmx_crc32; - - if (!dvbdemux->memcopy) - dvbdemux->memcopy = dvb_dmx_memcopy; - - dmx->frontend = NULL; - dmx->priv = dvbdemux; - 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_section_feed = dvbdmx_allocate_section_feed; - dmx->release_section_feed = dvbdmx_release_section_feed; - - 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; - - mutex_init(&dvbdemux->mutex); - spin_lock_init(&dvbdemux->lock); - - return 0; -} - -EXPORT_SYMBOL(dvb_dmx_init); - -void dvb_dmx_release(struct dvb_demux *dvbdemux) -{ - vfree(dvbdemux->cnt_storage); - vfree(dvbdemux->filter); - vfree(dvbdemux->feed); -} - -EXPORT_SYMBOL(dvb_dmx_release); |