Path: blob/master/drivers/media/dvb/dvb-core/dvb_demux.c
15112 views
/*1* dvb_demux.c - DVB kernel demux API2*3* Copyright (C) 2000-2001 Ralph Metzler <[email protected]>4* & Marcus Metzler <[email protected]>5* for convergence integrated media GmbH6*7* This program is free software; you can redistribute it and/or8* modify it under the terms of the GNU Lesser General Public License9* as published by the Free Software Foundation; either version 2.110* of the License, or (at your option) any later version.11*12* This program is distributed in the hope that it will be useful,13* but WITHOUT ANY WARRANTY; without even the implied warranty of14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the15* GNU General Public License for more details.16*17* You should have received a copy of the GNU Lesser General Public License18* along with this program; if not, write to the Free Software19* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.20*21*/2223#include <linux/sched.h>24#include <linux/spinlock.h>25#include <linux/slab.h>26#include <linux/vmalloc.h>27#include <linux/module.h>28#include <linux/poll.h>29#include <linux/string.h>30#include <linux/crc32.h>31#include <asm/uaccess.h>32#include <asm/div64.h>3334#include "dvb_demux.h"3536#define NOBUFS37/*38** #define DVB_DEMUX_SECTION_LOSS_LOG to monitor payload loss in the syslog39*/40// #define DVB_DEMUX_SECTION_LOSS_LOG4142static int dvb_demux_tscheck;43module_param(dvb_demux_tscheck, int, 0644);44MODULE_PARM_DESC(dvb_demux_tscheck,45"enable transport stream continuity and TEI check");4647static int dvb_demux_speedcheck;48module_param(dvb_demux_speedcheck, int, 0644);49MODULE_PARM_DESC(dvb_demux_speedcheck,50"enable transport stream speed check");5152#define dprintk_tscheck(x...) do { \53if (dvb_demux_tscheck && printk_ratelimit()) \54printk(x); \55} while (0)5657/******************************************************************************58* static inlined helper functions59******************************************************************************/6061static inline u16 section_length(const u8 *buf)62{63return 3 + ((buf[1] & 0x0f) << 8) + buf[2];64}6566static inline u16 ts_pid(const u8 *buf)67{68return ((buf[1] & 0x1f) << 8) + buf[2];69}7071static inline u8 payload(const u8 *tsp)72{73if (!(tsp[3] & 0x10)) // no payload?74return 0;7576if (tsp[3] & 0x20) { // adaptation field?77if (tsp[4] > 183) // corrupted data?78return 0;79else80return 184 - 1 - tsp[4];81}8283return 184;84}8586static u32 dvb_dmx_crc32(struct dvb_demux_feed *f, const u8 *src, size_t len)87{88return (f->feed.sec.crc_val = crc32_be(f->feed.sec.crc_val, src, len));89}9091static void dvb_dmx_memcopy(struct dvb_demux_feed *f, u8 *d, const u8 *s,92size_t len)93{94memcpy(d, s, len);95}9697/******************************************************************************98* Software filter functions99******************************************************************************/100101static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed,102const u8 *buf)103{104int count = payload(buf);105int p;106//int ccok;107//u8 cc;108109if (count == 0)110return -1;111112p = 188 - count;113114/*115cc = buf[3] & 0x0f;116ccok = ((feed->cc + 1) & 0x0f) == cc;117feed->cc = cc;118if (!ccok)119printk("missed packet!\n");120*/121122if (buf[1] & 0x40) // PUSI ?123feed->peslen = 0xfffa;124125feed->peslen += count;126127return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts, DMX_OK);128}129130static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed,131struct dvb_demux_filter *f)132{133u8 neq = 0;134int i;135136for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {137u8 xor = f->filter.filter_value[i] ^ feed->feed.sec.secbuf[i];138139if (f->maskandmode[i] & xor)140return 0;141142neq |= f->maskandnotmode[i] & xor;143}144145if (f->doneq && !neq)146return 0;147148return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen,149NULL, 0, &f->filter, DMX_OK);150}151152static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed)153{154struct dvb_demux *demux = feed->demux;155struct dvb_demux_filter *f = feed->filter;156struct dmx_section_feed *sec = &feed->feed.sec;157int section_syntax_indicator;158159if (!sec->is_filtering)160return 0;161162if (!f)163return 0;164165if (sec->check_crc) {166section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0);167if (section_syntax_indicator &&168demux->check_crc32(feed, sec->secbuf, sec->seclen))169return -1;170}171172do {173if (dvb_dmx_swfilter_sectionfilter(feed, f) < 0)174return -1;175} while ((f = f->next) && sec->is_filtering);176177sec->seclen = 0;178179return 0;180}181182static void dvb_dmx_swfilter_section_new(struct dvb_demux_feed *feed)183{184struct dmx_section_feed *sec = &feed->feed.sec;185186#ifdef DVB_DEMUX_SECTION_LOSS_LOG187if (sec->secbufp < sec->tsfeedp) {188int i, n = sec->tsfeedp - sec->secbufp;189190/*191* Section padding is done with 0xff bytes entirely.192* Due to speed reasons, we won't check all of them193* but just first and last.194*/195if (sec->secbuf[0] != 0xff || sec->secbuf[n - 1] != 0xff) {196printk("dvb_demux.c section ts padding loss: %d/%d\n",197n, sec->tsfeedp);198printk("dvb_demux.c pad data:");199for (i = 0; i < n; i++)200printk(" %02x", sec->secbuf[i]);201printk("\n");202}203}204#endif205206sec->tsfeedp = sec->secbufp = sec->seclen = 0;207sec->secbuf = sec->secbuf_base;208}209210/*211* Losless Section Demux 1.4.1 by Emard212* Valsecchi Patrick:213* - middle of section A (no PUSI)214* - end of section A and start of section B215* (with PUSI pointing to the start of the second section)216*217* In this case, without feed->pusi_seen you'll receive a garbage section218* consisting of the end of section A. Basically because tsfeedp219* is incemented and the use=0 condition is not raised220* when the second packet arrives.221*222* Fix:223* when demux is started, let feed->pusi_seen = 0 to224* prevent initial feeding of garbage from the end of225* previous section. When you for the first time see PUSI=1226* then set feed->pusi_seen = 1227*/228static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed,229const u8 *buf, u8 len)230{231struct dvb_demux *demux = feed->demux;232struct dmx_section_feed *sec = &feed->feed.sec;233u16 limit, seclen, n;234235if (sec->tsfeedp >= DMX_MAX_SECFEED_SIZE)236return 0;237238if (sec->tsfeedp + len > DMX_MAX_SECFEED_SIZE) {239#ifdef DVB_DEMUX_SECTION_LOSS_LOG240printk("dvb_demux.c section buffer full loss: %d/%d\n",241sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE,242DMX_MAX_SECFEED_SIZE);243#endif244len = DMX_MAX_SECFEED_SIZE - sec->tsfeedp;245}246247if (len <= 0)248return 0;249250demux->memcopy(feed, sec->secbuf_base + sec->tsfeedp, buf, len);251sec->tsfeedp += len;252253/*254* Dump all the sections we can find in the data (Emard)255*/256limit = sec->tsfeedp;257if (limit > DMX_MAX_SECFEED_SIZE)258return -1; /* internal error should never happen */259260/* to be sure always set secbuf */261sec->secbuf = sec->secbuf_base + sec->secbufp;262263for (n = 0; sec->secbufp + 2 < limit; n++) {264seclen = section_length(sec->secbuf);265if (seclen <= 0 || seclen > DMX_MAX_SECTION_SIZE266|| seclen + sec->secbufp > limit)267return 0;268sec->seclen = seclen;269sec->crc_val = ~0;270/* dump [secbuf .. secbuf+seclen) */271if (feed->pusi_seen)272dvb_dmx_swfilter_section_feed(feed);273#ifdef DVB_DEMUX_SECTION_LOSS_LOG274else275printk("dvb_demux.c pusi not seen, discarding section data\n");276#endif277sec->secbufp += seclen; /* secbufp and secbuf moving together is */278sec->secbuf += seclen; /* redundant but saves pointer arithmetic */279}280281return 0;282}283284static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,285const u8 *buf)286{287u8 p, count;288int ccok, dc_i = 0;289u8 cc;290291count = payload(buf);292293if (count == 0) /* count == 0 if no payload or out of range */294return -1;295296p = 188 - count; /* payload start */297298cc = buf[3] & 0x0f;299ccok = ((feed->cc + 1) & 0x0f) == cc;300feed->cc = cc;301302if (buf[3] & 0x20) {303/* adaption field present, check for discontinuity_indicator */304if ((buf[4] > 0) && (buf[5] & 0x80))305dc_i = 1;306}307308if (!ccok || dc_i) {309#ifdef DVB_DEMUX_SECTION_LOSS_LOG310printk("dvb_demux.c discontinuity detected %d bytes lost\n",311count);312/*313* those bytes under sume circumstances will again be reported314* in the following dvb_dmx_swfilter_section_new315*/316#endif317/*318* Discontinuity detected. Reset pusi_seen = 0 to319* stop feeding of suspicious data until next PUSI=1 arrives320*/321feed->pusi_seen = 0;322dvb_dmx_swfilter_section_new(feed);323}324325if (buf[1] & 0x40) {326/* PUSI=1 (is set), section boundary is here */327if (count > 1 && buf[p] < count) {328const u8 *before = &buf[p + 1];329u8 before_len = buf[p];330const u8 *after = &before[before_len];331u8 after_len = count - 1 - before_len;332333dvb_dmx_swfilter_section_copy_dump(feed, before,334before_len);335/* before start of new section, set pusi_seen = 1 */336feed->pusi_seen = 1;337dvb_dmx_swfilter_section_new(feed);338dvb_dmx_swfilter_section_copy_dump(feed, after,339after_len);340}341#ifdef DVB_DEMUX_SECTION_LOSS_LOG342else if (count > 0)343printk("dvb_demux.c PUSI=1 but %d bytes lost\n", count);344#endif345} else {346/* PUSI=0 (is not set), no section boundary */347dvb_dmx_swfilter_section_copy_dump(feed, &buf[p], count);348}349350return 0;351}352353static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed,354const u8 *buf)355{356switch (feed->type) {357case DMX_TYPE_TS:358if (!feed->feed.ts.is_filtering)359break;360if (feed->ts_type & TS_PACKET) {361if (feed->ts_type & TS_PAYLOAD_ONLY)362dvb_dmx_swfilter_payload(feed, buf);363else364feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts,365DMX_OK);366}367if (feed->ts_type & TS_DECODER)368if (feed->demux->write_to_decoder)369feed->demux->write_to_decoder(feed, buf, 188);370break;371372case DMX_TYPE_SEC:373if (!feed->feed.sec.is_filtering)374break;375if (dvb_dmx_swfilter_section_packet(feed, buf) < 0)376feed->feed.sec.seclen = feed->feed.sec.secbufp = 0;377break;378379default:380break;381}382}383384#define DVR_FEED(f) \385(((f)->type == DMX_TYPE_TS) && \386((f)->feed.ts.is_filtering) && \387(((f)->ts_type & (TS_PACKET | TS_DEMUX)) == TS_PACKET))388389static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)390{391struct dvb_demux_feed *feed;392u16 pid = ts_pid(buf);393int dvr_done = 0;394395if (dvb_demux_speedcheck) {396struct timespec cur_time, delta_time;397u64 speed_bytes, speed_timedelta;398399demux->speed_pkts_cnt++;400401/* show speed every SPEED_PKTS_INTERVAL packets */402if (!(demux->speed_pkts_cnt % SPEED_PKTS_INTERVAL)) {403cur_time = current_kernel_time();404405if (demux->speed_last_time.tv_sec != 0 &&406demux->speed_last_time.tv_nsec != 0) {407delta_time = timespec_sub(cur_time,408demux->speed_last_time);409speed_bytes = (u64)demux->speed_pkts_cnt410* 188 * 8;411/* convert to 1024 basis */412speed_bytes = 1000 * div64_u64(speed_bytes,4131024);414speed_timedelta =415(u64)timespec_to_ns(&delta_time);416speed_timedelta = div64_u64(speed_timedelta,4171000000); /* nsec -> usec */418printk(KERN_INFO "TS speed %llu Kbits/sec \n",419div64_u64(speed_bytes,420speed_timedelta));421};422423demux->speed_last_time = cur_time;424demux->speed_pkts_cnt = 0;425};426};427428if (demux->cnt_storage && dvb_demux_tscheck) {429/* check pkt counter */430if (pid < MAX_PID) {431if (buf[1] & 0x80)432dprintk_tscheck("TEI detected. "433"PID=0x%x data1=0x%x\n",434pid, buf[1]);435436if ((buf[3] & 0xf) != demux->cnt_storage[pid])437dprintk_tscheck("TS packet counter mismatch. "438"PID=0x%x expected 0x%x "439"got 0x%x\n",440pid, demux->cnt_storage[pid],441buf[3] & 0xf);442443demux->cnt_storage[pid] = ((buf[3] & 0xf) + 1)&0xf;444};445/* end check */446};447448list_for_each_entry(feed, &demux->feed_list, list_head) {449if ((feed->pid != pid) && (feed->pid != 0x2000))450continue;451452/* copy each packet only once to the dvr device, even453* if a PID is in multiple filters (e.g. video + PCR) */454if ((DVR_FEED(feed)) && (dvr_done++))455continue;456457if (feed->pid == pid)458dvb_dmx_swfilter_packet_type(feed, buf);459else if (feed->pid == 0x2000)460feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);461}462}463464void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf,465size_t count)466{467spin_lock(&demux->lock);468469while (count--) {470if (buf[0] == 0x47)471dvb_dmx_swfilter_packet(demux, buf);472buf += 188;473}474475spin_unlock(&demux->lock);476}477478EXPORT_SYMBOL(dvb_dmx_swfilter_packets);479480static inline int find_next_packet(const u8 *buf, int pos, size_t count,481const int pktsize)482{483int start = pos, lost;484485while (pos < count) {486if (buf[pos] == 0x47 ||487(pktsize == 204 && buf[pos] == 0xB8))488break;489pos++;490}491492lost = pos - start;493if (lost) {494/* This garbage is part of a valid packet? */495int backtrack = pos - pktsize;496if (backtrack >= 0 && (buf[backtrack] == 0x47 ||497(pktsize == 204 && buf[backtrack] == 0xB8)))498return backtrack;499}500501return pos;502}503504/* Filter all pktsize= 188 or 204 sized packets and skip garbage. */505static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf,506size_t count, const int pktsize)507{508int p = 0, i, j;509const u8 *q;510511spin_lock(&demux->lock);512513if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */514i = demux->tsbufp;515j = pktsize - i;516if (count < j) {517memcpy(&demux->tsbuf[i], buf, count);518demux->tsbufp += count;519goto bailout;520}521memcpy(&demux->tsbuf[i], buf, j);522if (demux->tsbuf[0] == 0x47) /* double check */523dvb_dmx_swfilter_packet(demux, demux->tsbuf);524demux->tsbufp = 0;525p += j;526}527528while (1) {529p = find_next_packet(buf, p, count, pktsize);530if (p >= count)531break;532if (count - p < pktsize)533break;534535q = &buf[p];536537if (pktsize == 204 && (*q == 0xB8)) {538memcpy(demux->tsbuf, q, 188);539demux->tsbuf[0] = 0x47;540q = demux->tsbuf;541}542dvb_dmx_swfilter_packet(demux, q);543p += pktsize;544}545546i = count - p;547if (i) {548memcpy(demux->tsbuf, &buf[p], i);549demux->tsbufp = i;550if (pktsize == 204 && demux->tsbuf[0] == 0xB8)551demux->tsbuf[0] = 0x47;552}553554bailout:555spin_unlock(&demux->lock);556}557558void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count)559{560_dvb_dmx_swfilter(demux, buf, count, 188);561}562EXPORT_SYMBOL(dvb_dmx_swfilter);563564void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count)565{566_dvb_dmx_swfilter(demux, buf, count, 204);567}568EXPORT_SYMBOL(dvb_dmx_swfilter_204);569570static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux)571{572int i;573574for (i = 0; i < demux->filternum; i++)575if (demux->filter[i].state == DMX_STATE_FREE)576break;577578if (i == demux->filternum)579return NULL;580581demux->filter[i].state = DMX_STATE_ALLOCATED;582583return &demux->filter[i];584}585586static struct dvb_demux_feed *dvb_dmx_feed_alloc(struct dvb_demux *demux)587{588int i;589590for (i = 0; i < demux->feednum; i++)591if (demux->feed[i].state == DMX_STATE_FREE)592break;593594if (i == demux->feednum)595return NULL;596597demux->feed[i].state = DMX_STATE_ALLOCATED;598599return &demux->feed[i];600}601602static int dvb_demux_feed_find(struct dvb_demux_feed *feed)603{604struct dvb_demux_feed *entry;605606list_for_each_entry(entry, &feed->demux->feed_list, list_head)607if (entry == feed)608return 1;609610return 0;611}612613static void dvb_demux_feed_add(struct dvb_demux_feed *feed)614{615spin_lock_irq(&feed->demux->lock);616if (dvb_demux_feed_find(feed)) {617printk(KERN_ERR "%s: feed already in list (type=%x state=%x pid=%x)\n",618__func__, feed->type, feed->state, feed->pid);619goto out;620}621622list_add(&feed->list_head, &feed->demux->feed_list);623out:624spin_unlock_irq(&feed->demux->lock);625}626627static void dvb_demux_feed_del(struct dvb_demux_feed *feed)628{629spin_lock_irq(&feed->demux->lock);630if (!(dvb_demux_feed_find(feed))) {631printk(KERN_ERR "%s: feed not in list (type=%x state=%x pid=%x)\n",632__func__, feed->type, feed->state, feed->pid);633goto out;634}635636list_del(&feed->list_head);637out:638spin_unlock_irq(&feed->demux->lock);639}640641static int dmx_ts_feed_set(struct dmx_ts_feed *ts_feed, u16 pid, int ts_type,642enum dmx_ts_pes pes_type,643size_t circular_buffer_size, struct timespec timeout)644{645struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;646struct dvb_demux *demux = feed->demux;647648if (pid > DMX_MAX_PID)649return -EINVAL;650651if (mutex_lock_interruptible(&demux->mutex))652return -ERESTARTSYS;653654if (ts_type & TS_DECODER) {655if (pes_type >= DMX_TS_PES_OTHER) {656mutex_unlock(&demux->mutex);657return -EINVAL;658}659660if (demux->pesfilter[pes_type] &&661demux->pesfilter[pes_type] != feed) {662mutex_unlock(&demux->mutex);663return -EINVAL;664}665666demux->pesfilter[pes_type] = feed;667demux->pids[pes_type] = pid;668}669670dvb_demux_feed_add(feed);671672feed->pid = pid;673feed->buffer_size = circular_buffer_size;674feed->timeout = timeout;675feed->ts_type = ts_type;676feed->pes_type = pes_type;677678if (feed->buffer_size) {679#ifdef NOBUFS680feed->buffer = NULL;681#else682feed->buffer = vmalloc(feed->buffer_size);683if (!feed->buffer) {684mutex_unlock(&demux->mutex);685return -ENOMEM;686}687#endif688}689690feed->state = DMX_STATE_READY;691mutex_unlock(&demux->mutex);692693return 0;694}695696static int dmx_ts_feed_start_filtering(struct dmx_ts_feed *ts_feed)697{698struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;699struct dvb_demux *demux = feed->demux;700int ret;701702if (mutex_lock_interruptible(&demux->mutex))703return -ERESTARTSYS;704705if (feed->state != DMX_STATE_READY || feed->type != DMX_TYPE_TS) {706mutex_unlock(&demux->mutex);707return -EINVAL;708}709710if (!demux->start_feed) {711mutex_unlock(&demux->mutex);712return -ENODEV;713}714715if ((ret = demux->start_feed(feed)) < 0) {716mutex_unlock(&demux->mutex);717return ret;718}719720spin_lock_irq(&demux->lock);721ts_feed->is_filtering = 1;722feed->state = DMX_STATE_GO;723spin_unlock_irq(&demux->lock);724mutex_unlock(&demux->mutex);725726return 0;727}728729static int dmx_ts_feed_stop_filtering(struct dmx_ts_feed *ts_feed)730{731struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;732struct dvb_demux *demux = feed->demux;733int ret;734735mutex_lock(&demux->mutex);736737if (feed->state < DMX_STATE_GO) {738mutex_unlock(&demux->mutex);739return -EINVAL;740}741742if (!demux->stop_feed) {743mutex_unlock(&demux->mutex);744return -ENODEV;745}746747ret = demux->stop_feed(feed);748749spin_lock_irq(&demux->lock);750ts_feed->is_filtering = 0;751feed->state = DMX_STATE_ALLOCATED;752spin_unlock_irq(&demux->lock);753mutex_unlock(&demux->mutex);754755return ret;756}757758static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,759struct dmx_ts_feed **ts_feed,760dmx_ts_cb callback)761{762struct dvb_demux *demux = (struct dvb_demux *)dmx;763struct dvb_demux_feed *feed;764765if (mutex_lock_interruptible(&demux->mutex))766return -ERESTARTSYS;767768if (!(feed = dvb_dmx_feed_alloc(demux))) {769mutex_unlock(&demux->mutex);770return -EBUSY;771}772773feed->type = DMX_TYPE_TS;774feed->cb.ts = callback;775feed->demux = demux;776feed->pid = 0xffff;777feed->peslen = 0xfffa;778feed->buffer = NULL;779780(*ts_feed) = &feed->feed.ts;781(*ts_feed)->parent = dmx;782(*ts_feed)->priv = NULL;783(*ts_feed)->is_filtering = 0;784(*ts_feed)->start_filtering = dmx_ts_feed_start_filtering;785(*ts_feed)->stop_filtering = dmx_ts_feed_stop_filtering;786(*ts_feed)->set = dmx_ts_feed_set;787788if (!(feed->filter = dvb_dmx_filter_alloc(demux))) {789feed->state = DMX_STATE_FREE;790mutex_unlock(&demux->mutex);791return -EBUSY;792}793794feed->filter->type = DMX_TYPE_TS;795feed->filter->feed = feed;796feed->filter->state = DMX_STATE_READY;797798mutex_unlock(&demux->mutex);799800return 0;801}802803static int dvbdmx_release_ts_feed(struct dmx_demux *dmx,804struct dmx_ts_feed *ts_feed)805{806struct dvb_demux *demux = (struct dvb_demux *)dmx;807struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;808809mutex_lock(&demux->mutex);810811if (feed->state == DMX_STATE_FREE) {812mutex_unlock(&demux->mutex);813return -EINVAL;814}815#ifndef NOBUFS816vfree(feed->buffer);817feed->buffer = NULL;818#endif819820feed->state = DMX_STATE_FREE;821feed->filter->state = DMX_STATE_FREE;822823dvb_demux_feed_del(feed);824825feed->pid = 0xffff;826827if (feed->ts_type & TS_DECODER && feed->pes_type < DMX_TS_PES_OTHER)828demux->pesfilter[feed->pes_type] = NULL;829830mutex_unlock(&demux->mutex);831return 0;832}833834/******************************************************************************835* dmx_section_feed API calls836******************************************************************************/837838static int dmx_section_feed_allocate_filter(struct dmx_section_feed *feed,839struct dmx_section_filter **filter)840{841struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;842struct dvb_demux *dvbdemux = dvbdmxfeed->demux;843struct dvb_demux_filter *dvbdmxfilter;844845if (mutex_lock_interruptible(&dvbdemux->mutex))846return -ERESTARTSYS;847848dvbdmxfilter = dvb_dmx_filter_alloc(dvbdemux);849if (!dvbdmxfilter) {850mutex_unlock(&dvbdemux->mutex);851return -EBUSY;852}853854spin_lock_irq(&dvbdemux->lock);855*filter = &dvbdmxfilter->filter;856(*filter)->parent = feed;857(*filter)->priv = NULL;858dvbdmxfilter->feed = dvbdmxfeed;859dvbdmxfilter->type = DMX_TYPE_SEC;860dvbdmxfilter->state = DMX_STATE_READY;861dvbdmxfilter->next = dvbdmxfeed->filter;862dvbdmxfeed->filter = dvbdmxfilter;863spin_unlock_irq(&dvbdemux->lock);864865mutex_unlock(&dvbdemux->mutex);866return 0;867}868869static int dmx_section_feed_set(struct dmx_section_feed *feed,870u16 pid, size_t circular_buffer_size,871int check_crc)872{873struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;874struct dvb_demux *dvbdmx = dvbdmxfeed->demux;875876if (pid > 0x1fff)877return -EINVAL;878879if (mutex_lock_interruptible(&dvbdmx->mutex))880return -ERESTARTSYS;881882dvb_demux_feed_add(dvbdmxfeed);883884dvbdmxfeed->pid = pid;885dvbdmxfeed->buffer_size = circular_buffer_size;886dvbdmxfeed->feed.sec.check_crc = check_crc;887888#ifdef NOBUFS889dvbdmxfeed->buffer = NULL;890#else891dvbdmxfeed->buffer = vmalloc(dvbdmxfeed->buffer_size);892if (!dvbdmxfeed->buffer) {893mutex_unlock(&dvbdmx->mutex);894return -ENOMEM;895}896#endif897898dvbdmxfeed->state = DMX_STATE_READY;899mutex_unlock(&dvbdmx->mutex);900return 0;901}902903static void prepare_secfilters(struct dvb_demux_feed *dvbdmxfeed)904{905int i;906struct dvb_demux_filter *f;907struct dmx_section_filter *sf;908u8 mask, mode, doneq;909910if (!(f = dvbdmxfeed->filter))911return;912do {913sf = &f->filter;914doneq = 0;915for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {916mode = sf->filter_mode[i];917mask = sf->filter_mask[i];918f->maskandmode[i] = mask & mode;919doneq |= f->maskandnotmode[i] = mask & ~mode;920}921f->doneq = doneq ? 1 : 0;922} while ((f = f->next));923}924925static int dmx_section_feed_start_filtering(struct dmx_section_feed *feed)926{927struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;928struct dvb_demux *dvbdmx = dvbdmxfeed->demux;929int ret;930931if (mutex_lock_interruptible(&dvbdmx->mutex))932return -ERESTARTSYS;933934if (feed->is_filtering) {935mutex_unlock(&dvbdmx->mutex);936return -EBUSY;937}938939if (!dvbdmxfeed->filter) {940mutex_unlock(&dvbdmx->mutex);941return -EINVAL;942}943944dvbdmxfeed->feed.sec.tsfeedp = 0;945dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base;946dvbdmxfeed->feed.sec.secbufp = 0;947dvbdmxfeed->feed.sec.seclen = 0;948949if (!dvbdmx->start_feed) {950mutex_unlock(&dvbdmx->mutex);951return -ENODEV;952}953954prepare_secfilters(dvbdmxfeed);955956if ((ret = dvbdmx->start_feed(dvbdmxfeed)) < 0) {957mutex_unlock(&dvbdmx->mutex);958return ret;959}960961spin_lock_irq(&dvbdmx->lock);962feed->is_filtering = 1;963dvbdmxfeed->state = DMX_STATE_GO;964spin_unlock_irq(&dvbdmx->lock);965966mutex_unlock(&dvbdmx->mutex);967return 0;968}969970static int dmx_section_feed_stop_filtering(struct dmx_section_feed *feed)971{972struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;973struct dvb_demux *dvbdmx = dvbdmxfeed->demux;974int ret;975976mutex_lock(&dvbdmx->mutex);977978if (!dvbdmx->stop_feed) {979mutex_unlock(&dvbdmx->mutex);980return -ENODEV;981}982983ret = dvbdmx->stop_feed(dvbdmxfeed);984985spin_lock_irq(&dvbdmx->lock);986dvbdmxfeed->state = DMX_STATE_READY;987feed->is_filtering = 0;988spin_unlock_irq(&dvbdmx->lock);989990mutex_unlock(&dvbdmx->mutex);991return ret;992}993994static int dmx_section_feed_release_filter(struct dmx_section_feed *feed,995struct dmx_section_filter *filter)996{997struct dvb_demux_filter *dvbdmxfilter = (struct dvb_demux_filter *)filter, *f;998struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;999struct dvb_demux *dvbdmx = dvbdmxfeed->demux;10001001mutex_lock(&dvbdmx->mutex);10021003if (dvbdmxfilter->feed != dvbdmxfeed) {1004mutex_unlock(&dvbdmx->mutex);1005return -EINVAL;1006}10071008if (feed->is_filtering)1009feed->stop_filtering(feed);10101011spin_lock_irq(&dvbdmx->lock);1012f = dvbdmxfeed->filter;10131014if (f == dvbdmxfilter) {1015dvbdmxfeed->filter = dvbdmxfilter->next;1016} else {1017while (f->next != dvbdmxfilter)1018f = f->next;1019f->next = f->next->next;1020}10211022dvbdmxfilter->state = DMX_STATE_FREE;1023spin_unlock_irq(&dvbdmx->lock);1024mutex_unlock(&dvbdmx->mutex);1025return 0;1026}10271028static int dvbdmx_allocate_section_feed(struct dmx_demux *demux,1029struct dmx_section_feed **feed,1030dmx_section_cb callback)1031{1032struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;1033struct dvb_demux_feed *dvbdmxfeed;10341035if (mutex_lock_interruptible(&dvbdmx->mutex))1036return -ERESTARTSYS;10371038if (!(dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx))) {1039mutex_unlock(&dvbdmx->mutex);1040return -EBUSY;1041}10421043dvbdmxfeed->type = DMX_TYPE_SEC;1044dvbdmxfeed->cb.sec = callback;1045dvbdmxfeed->demux = dvbdmx;1046dvbdmxfeed->pid = 0xffff;1047dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base;1048dvbdmxfeed->feed.sec.secbufp = dvbdmxfeed->feed.sec.seclen = 0;1049dvbdmxfeed->feed.sec.tsfeedp = 0;1050dvbdmxfeed->filter = NULL;1051dvbdmxfeed->buffer = NULL;10521053(*feed) = &dvbdmxfeed->feed.sec;1054(*feed)->is_filtering = 0;1055(*feed)->parent = demux;1056(*feed)->priv = NULL;10571058(*feed)->set = dmx_section_feed_set;1059(*feed)->allocate_filter = dmx_section_feed_allocate_filter;1060(*feed)->start_filtering = dmx_section_feed_start_filtering;1061(*feed)->stop_filtering = dmx_section_feed_stop_filtering;1062(*feed)->release_filter = dmx_section_feed_release_filter;10631064mutex_unlock(&dvbdmx->mutex);1065return 0;1066}10671068static int dvbdmx_release_section_feed(struct dmx_demux *demux,1069struct dmx_section_feed *feed)1070{1071struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;1072struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;10731074mutex_lock(&dvbdmx->mutex);10751076if (dvbdmxfeed->state == DMX_STATE_FREE) {1077mutex_unlock(&dvbdmx->mutex);1078return -EINVAL;1079}1080#ifndef NOBUFS1081vfree(dvbdmxfeed->buffer);1082dvbdmxfeed->buffer = NULL;1083#endif1084dvbdmxfeed->state = DMX_STATE_FREE;10851086dvb_demux_feed_del(dvbdmxfeed);10871088dvbdmxfeed->pid = 0xffff;10891090mutex_unlock(&dvbdmx->mutex);1091return 0;1092}10931094/******************************************************************************1095* dvb_demux kernel data API calls1096******************************************************************************/10971098static int dvbdmx_open(struct dmx_demux *demux)1099{1100struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;11011102if (dvbdemux->users >= MAX_DVB_DEMUX_USERS)1103return -EUSERS;11041105dvbdemux->users++;1106return 0;1107}11081109static int dvbdmx_close(struct dmx_demux *demux)1110{1111struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;11121113if (dvbdemux->users == 0)1114return -ENODEV;11151116dvbdemux->users--;1117//FIXME: release any unneeded resources if users==01118return 0;1119}11201121static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t count)1122{1123struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;1124void *p;11251126if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE))1127return -EINVAL;11281129p = memdup_user(buf, count);1130if (IS_ERR(p))1131return PTR_ERR(p);1132if (mutex_lock_interruptible(&dvbdemux->mutex)) {1133kfree(p);1134return -ERESTARTSYS;1135}1136dvb_dmx_swfilter(dvbdemux, p, count);1137kfree(p);1138mutex_unlock(&dvbdemux->mutex);11391140if (signal_pending(current))1141return -EINTR;1142return count;1143}11441145static int dvbdmx_add_frontend(struct dmx_demux *demux,1146struct dmx_frontend *frontend)1147{1148struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;1149struct list_head *head = &dvbdemux->frontend_list;11501151list_add(&(frontend->connectivity_list), head);11521153return 0;1154}11551156static int dvbdmx_remove_frontend(struct dmx_demux *demux,1157struct dmx_frontend *frontend)1158{1159struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;1160struct list_head *pos, *n, *head = &dvbdemux->frontend_list;11611162list_for_each_safe(pos, n, head) {1163if (DMX_FE_ENTRY(pos) == frontend) {1164list_del(pos);1165return 0;1166}1167}11681169return -ENODEV;1170}11711172static struct list_head *dvbdmx_get_frontends(struct dmx_demux *demux)1173{1174struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;11751176if (list_empty(&dvbdemux->frontend_list))1177return NULL;11781179return &dvbdemux->frontend_list;1180}11811182static int dvbdmx_connect_frontend(struct dmx_demux *demux,1183struct dmx_frontend *frontend)1184{1185struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;11861187if (demux->frontend)1188return -EINVAL;11891190mutex_lock(&dvbdemux->mutex);11911192demux->frontend = frontend;1193mutex_unlock(&dvbdemux->mutex);1194return 0;1195}11961197static int dvbdmx_disconnect_frontend(struct dmx_demux *demux)1198{1199struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;12001201mutex_lock(&dvbdemux->mutex);12021203demux->frontend = NULL;1204mutex_unlock(&dvbdemux->mutex);1205return 0;1206}12071208static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 * pids)1209{1210struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;12111212memcpy(pids, dvbdemux->pids, 5 * sizeof(u16));1213return 0;1214}12151216int dvb_dmx_init(struct dvb_demux *dvbdemux)1217{1218int i;1219struct dmx_demux *dmx = &dvbdemux->dmx;12201221dvbdemux->cnt_storage = NULL;1222dvbdemux->users = 0;1223dvbdemux->filter = vmalloc(dvbdemux->filternum * sizeof(struct dvb_demux_filter));12241225if (!dvbdemux->filter)1226return -ENOMEM;12271228dvbdemux->feed = vmalloc(dvbdemux->feednum * sizeof(struct dvb_demux_feed));1229if (!dvbdemux->feed) {1230vfree(dvbdemux->filter);1231dvbdemux->filter = NULL;1232return -ENOMEM;1233}1234for (i = 0; i < dvbdemux->filternum; i++) {1235dvbdemux->filter[i].state = DMX_STATE_FREE;1236dvbdemux->filter[i].index = i;1237}1238for (i = 0; i < dvbdemux->feednum; i++) {1239dvbdemux->feed[i].state = DMX_STATE_FREE;1240dvbdemux->feed[i].index = i;1241}12421243dvbdemux->cnt_storage = vmalloc(MAX_PID + 1);1244if (!dvbdemux->cnt_storage)1245printk(KERN_WARNING "Couldn't allocate memory for TS/TEI check. Disabling it\n");12461247INIT_LIST_HEAD(&dvbdemux->frontend_list);12481249for (i = 0; i < DMX_TS_PES_OTHER; i++) {1250dvbdemux->pesfilter[i] = NULL;1251dvbdemux->pids[i] = 0xffff;1252}12531254INIT_LIST_HEAD(&dvbdemux->feed_list);12551256dvbdemux->playing = 0;1257dvbdemux->recording = 0;1258dvbdemux->tsbufp = 0;12591260if (!dvbdemux->check_crc32)1261dvbdemux->check_crc32 = dvb_dmx_crc32;12621263if (!dvbdemux->memcopy)1264dvbdemux->memcopy = dvb_dmx_memcopy;12651266dmx->frontend = NULL;1267dmx->priv = dvbdemux;1268dmx->open = dvbdmx_open;1269dmx->close = dvbdmx_close;1270dmx->write = dvbdmx_write;1271dmx->allocate_ts_feed = dvbdmx_allocate_ts_feed;1272dmx->release_ts_feed = dvbdmx_release_ts_feed;1273dmx->allocate_section_feed = dvbdmx_allocate_section_feed;1274dmx->release_section_feed = dvbdmx_release_section_feed;12751276dmx->add_frontend = dvbdmx_add_frontend;1277dmx->remove_frontend = dvbdmx_remove_frontend;1278dmx->get_frontends = dvbdmx_get_frontends;1279dmx->connect_frontend = dvbdmx_connect_frontend;1280dmx->disconnect_frontend = dvbdmx_disconnect_frontend;1281dmx->get_pes_pids = dvbdmx_get_pes_pids;12821283mutex_init(&dvbdemux->mutex);1284spin_lock_init(&dvbdemux->lock);12851286return 0;1287}12881289EXPORT_SYMBOL(dvb_dmx_init);12901291void dvb_dmx_release(struct dvb_demux *dvbdemux)1292{1293vfree(dvbdemux->cnt_storage);1294vfree(dvbdemux->filter);1295vfree(dvbdemux->feed);1296}12971298EXPORT_SYMBOL(dvb_dmx_release);129913001301