2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2009-10-04 20:11:37 +08:00
|
|
|
#include <linux/sched.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#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>
|
2009-11-02 05:46:53 +08:00
|
|
|
#include <asm/div64.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#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
|
|
|
|
|
2009-06-06 20:31:34 +08:00
|
|
|
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");
|
|
|
|
|
2009-11-02 05:46:53 +08:00
|
|
|
static int dvb_demux_speedcheck;
|
|
|
|
module_param(dvb_demux_speedcheck, int, 0644);
|
|
|
|
MODULE_PARM_DESC(dvb_demux_speedcheck,
|
|
|
|
"enable transport stream speed check");
|
|
|
|
|
[media] DVB: improve handling of TS packets containing a raised TEI bit
When the TEI bit is raised, we should not trust any of the contents of
the packet in question, including but not limited to its PID number.
Considering that we don't trust the PID number of this packet, we should
not proceed to check the packet counter (if dvb_demux_tscheck is set).
We should expect to see at least one discontinuity after a bad packet is
received, so any time a TEI is detected, a following TS packet counter
mismatch is to be expected.
There is no real reason to ever allow bad packets to pass through the
kernel demux, other than for purposes of attempting error correction via
software or statistical information.
However, since we have always passed these bad packets though the demux,
we should not change this default behavior.
Without altering module options, this patch merely prevents the
TS packet counter check on packets containing a raised TEI.
If module option dvb_demux_feed_err_pkts is set to 0, the kernel demux
will drop these error packets entirely, preventing any possibility of
corruption caused by userspace programs that are expecting valid data.
Signed-off-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2012-05-22 04:47:15 +08:00
|
|
|
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)");
|
|
|
|
|
2009-06-06 20:31:34 +08:00
|
|
|
#define dprintk_tscheck(x...) do { \
|
|
|
|
if (dvb_demux_tscheck && printk_ratelimit()) \
|
|
|
|
printk(x); \
|
|
|
|
} while (0)
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/******************************************************************************
|
|
|
|
* static inlined helper functions
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
|
|
static inline u16 section_length(const u8 *buf)
|
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
return 3 + ((buf[1] & 0x0f) << 8) + buf[2];
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline u16 ts_pid(const u8 *buf)
|
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
return ((buf[1] & 0x1f) << 8) + buf[2];
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline u8 payload(const u8 *tsp)
|
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
if (!(tsp[3] & 0x10)) // no payload?
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
2005-09-10 04:02:26 +08:00
|
|
|
|
|
|
|
if (tsp[3] & 0x20) { // adaptation field?
|
|
|
|
if (tsp[4] > 183) // corrupted data?
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
else
|
2005-09-10 04:02:26 +08:00
|
|
|
return 184 - 1 - tsp[4];
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2005-09-10 04:02:26 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 184;
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static u32 dvb_dmx_crc32(struct dvb_demux_feed *f, const u8 *src, size_t len)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
return (f->feed.sec.crc_val = crc32_be(f->feed.sec.crc_val, src, len));
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static void dvb_dmx_memcopy(struct dvb_demux_feed *f, u8 *d, const u8 *s,
|
|
|
|
size_t len)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
memcpy(d, s, len);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* Software filter functions
|
|
|
|
******************************************************************************/
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed,
|
|
|
|
const u8 *buf)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int count = payload(buf);
|
|
|
|
int p;
|
|
|
|
//int ccok;
|
|
|
|
//u8 cc;
|
|
|
|
|
|
|
|
if (count == 0)
|
|
|
|
return -1;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
p = 188 - count;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
2005-09-10 04:02:26 +08:00
|
|
|
cc = buf[3] & 0x0f;
|
|
|
|
ccok = ((feed->cc + 1) & 0x0f) == cc;
|
|
|
|
feed->cc = cc;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!ccok)
|
|
|
|
printk("missed packet!\n");
|
|
|
|
*/
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
if (buf[1] & 0x40) // PUSI ?
|
2005-04-17 06:20:36 +08:00
|
|
|
feed->peslen = 0xfffa;
|
|
|
|
|
|
|
|
feed->peslen += count;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts, DMX_OK);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed,
|
|
|
|
struct dvb_demux_filter *f)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
u8 neq = 0;
|
|
|
|
int i;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {
|
2005-04-17 06:20:36 +08:00
|
|
|
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;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen,
|
|
|
|
NULL, 0, &f->filter, DMX_OK);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
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
|
2005-09-10 04:02:26 +08:00
|
|
|
if (sec->secbufp < sec->tsfeedp) {
|
2005-04-17 06:20:36 +08:00
|
|
|
int i, n = sec->tsfeedp - sec->secbufp;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
/*
|
|
|
|
* 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) {
|
2005-04-17 06:20:36 +08:00
|
|
|
printk("dvb_demux.c section ts padding loss: %d/%d\n",
|
|
|
|
n, sec->tsfeedp);
|
|
|
|
printk("dvb_demux.c pad data:");
|
2005-09-10 04:02:26 +08:00
|
|
|
for (i = 0; i < n; i++)
|
2005-04-17 06:20:36 +08:00
|
|
|
printk(" %02x", sec->secbuf[i]);
|
|
|
|
printk("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
sec->tsfeedp = sec->secbufp = sec->seclen = 0;
|
|
|
|
sec->secbuf = sec->secbuf_base;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-09-10 04:02:26 +08:00
|
|
|
* 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)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct dvb_demux *demux = feed->demux;
|
|
|
|
struct dmx_section_feed *sec = &feed->feed.sec;
|
|
|
|
u16 limit, seclen, n;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
if (sec->tsfeedp >= DMX_MAX_SECFEED_SIZE)
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
if (sec->tsfeedp + len > DMX_MAX_SECFEED_SIZE) {
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef DVB_DEMUX_SECTION_LOSS_LOG
|
|
|
|
printk("dvb_demux.c section buffer full loss: %d/%d\n",
|
2005-09-10 04:02:26 +08:00
|
|
|
sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE,
|
|
|
|
DMX_MAX_SECFEED_SIZE);
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
|
|
|
len = DMX_MAX_SECFEED_SIZE - sec->tsfeedp;
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
if (len <= 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
demux->memcopy(feed, sec->secbuf_base + sec->tsfeedp, buf, len);
|
|
|
|
sec->tsfeedp += len;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
/*
|
|
|
|
* Dump all the sections we can find in the data (Emard)
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
limit = sec->tsfeedp;
|
2005-09-10 04:02:26 +08:00
|
|
|
if (limit > DMX_MAX_SECFEED_SIZE)
|
|
|
|
return -1; /* internal error should never happen */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* to be sure always set secbuf */
|
|
|
|
sec->secbuf = sec->secbuf_base + sec->secbufp;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
for (n = 0; sec->secbufp + 2 < limit; n++) {
|
2005-04-17 06:20:36 +08:00
|
|
|
seclen = section_length(sec->secbuf);
|
2005-11-09 13:35:50 +08:00
|
|
|
if (seclen <= 0 || seclen > DMX_MAX_SECTION_SIZE
|
2005-09-10 04:02:26 +08:00
|
|
|
|| seclen + sec->secbufp > limit)
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
sec->seclen = seclen;
|
|
|
|
sec->crc_val = ~0;
|
|
|
|
/* dump [secbuf .. secbuf+seclen) */
|
2005-09-10 04:02:26 +08:00
|
|
|
if (feed->pusi_seen)
|
2005-04-17 06:20:36 +08:00
|
|
|
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
|
2005-09-10 04:02:26 +08:00
|
|
|
sec->secbufp += seclen; /* secbufp and secbuf moving together is */
|
|
|
|
sec->secbuf += seclen; /* redundant but saves pointer arithmetic */
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,
|
|
|
|
const u8 *buf)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
u8 p, count;
|
|
|
|
int ccok, dc_i = 0;
|
|
|
|
u8 cc;
|
|
|
|
|
|
|
|
count = payload(buf);
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
if (count == 0) /* count == 0 if no payload or out of range */
|
2005-04-17 06:20:36 +08:00
|
|
|
return -1;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
p = 188 - count; /* payload start */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
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
|
2005-09-10 04:02:26 +08:00
|
|
|
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
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
2005-09-10 04:02:26 +08:00
|
|
|
/*
|
|
|
|
* Discontinuity detected. Reset pusi_seen = 0 to
|
|
|
|
* stop feeding of suspicious data until next PUSI=1 arrives
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
feed->pusi_seen = 0;
|
|
|
|
dvb_dmx_swfilter_section_new(feed);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf[1] & 0x40) {
|
2005-09-10 04:02:26 +08:00
|
|
|
/* PUSI=1 (is set), section boundary is here */
|
2005-04-17 06:20:36 +08:00
|
|
|
if (count > 1 && buf[p] < count) {
|
2005-09-10 04:02:26 +08:00
|
|
|
const u8 *before = &buf[p + 1];
|
2005-04-17 06:20:36 +08:00
|
|
|
u8 before_len = buf[p];
|
2005-09-10 04:02:26 +08:00
|
|
|
const u8 *after = &before[before_len];
|
|
|
|
u8 after_len = count - 1 - before_len;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
dvb_dmx_swfilter_section_copy_dump(feed, before,
|
|
|
|
before_len);
|
2005-04-17 06:20:36 +08:00
|
|
|
/* before start of new section, set pusi_seen = 1 */
|
|
|
|
feed->pusi_seen = 1;
|
|
|
|
dvb_dmx_swfilter_section_new(feed);
|
2005-09-10 04:02:26 +08:00
|
|
|
dvb_dmx_swfilter_section_copy_dump(feed, after,
|
|
|
|
after_len);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
#ifdef DVB_DEMUX_SECTION_LOSS_LOG
|
2005-09-10 04:02:26 +08:00
|
|
|
else if (count > 0)
|
|
|
|
printk("dvb_demux.c PUSI=1 but %d bytes lost\n", count);
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
|
|
|
} else {
|
2005-09-10 04:02:26 +08:00
|
|
|
/* PUSI=0 (is not set), no section boundary */
|
|
|
|
dvb_dmx_swfilter_section_copy_dump(feed, &buf[p], count);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2005-09-10 04:02:26 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed,
|
|
|
|
const u8 *buf)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
switch (feed->type) {
|
2005-04-17 06:20:36 +08:00
|
|
|
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
|
2005-09-10 04:02:26 +08:00
|
|
|
feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts,
|
|
|
|
DMX_OK);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
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)
|
2005-09-10 04:02:26 +08:00
|
|
|
feed->feed.sec.seclen = feed->feed.sec.secbufp = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DVR_FEED(f) \
|
|
|
|
(((f)->type == DMX_TYPE_TS) && \
|
|
|
|
((f)->feed.ts.is_filtering) && \
|
2008-04-23 01:45:47 +08:00
|
|
|
(((f)->ts_type & (TS_PACKET | TS_DEMUX)) == TS_PACKET))
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2009-11-02 05:46:53 +08:00
|
|
|
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));
|
2012-09-07 22:24:44 +08:00
|
|
|
}
|
2009-11-02 05:46:53 +08:00
|
|
|
|
|
|
|
demux->speed_last_time = cur_time;
|
|
|
|
demux->speed_pkts_cnt = 0;
|
2012-09-07 22:24:44 +08:00
|
|
|
}
|
|
|
|
}
|
2009-11-02 05:46:53 +08:00
|
|
|
|
[media] DVB: improve handling of TS packets containing a raised TEI bit
When the TEI bit is raised, we should not trust any of the contents of
the packet in question, including but not limited to its PID number.
Considering that we don't trust the PID number of this packet, we should
not proceed to check the packet counter (if dvb_demux_tscheck is set).
We should expect to see at least one discontinuity after a bad packet is
received, so any time a TEI is detected, a following TS packet counter
mismatch is to be expected.
There is no real reason to ever allow bad packets to pass through the
kernel demux, other than for purposes of attempting error correction via
software or statistical information.
However, since we have always passed these bad packets though the demux,
we should not change this default behavior.
Without altering module options, this patch merely prevents the
TS packet counter check on packets containing a raised TEI.
If module option dvb_demux_feed_err_pkts is set to 0, the kernel demux
will drop these error packets entirely, preventing any possibility of
corruption caused by userspace programs that are expecting valid data.
Signed-off-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2012-05-22 04:47:15 +08:00
|
|
|
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 */
|
2010-03-07 01:58:01 +08:00
|
|
|
if (demux->cnt_storage && dvb_demux_tscheck) {
|
2009-06-06 20:31:34 +08:00
|
|
|
/* 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;
|
2012-09-07 22:24:44 +08:00
|
|
|
}
|
2009-06-06 20:31:34 +08:00
|
|
|
/* end check */
|
2012-09-07 22:24:44 +08:00
|
|
|
}
|
2009-06-06 20:31:34 +08:00
|
|
|
|
2007-10-10 16:37:39 +08:00
|
|
|
list_for_each_entry(feed, &demux->feed_list, list_head) {
|
2005-04-17 06:20:36 +08:00
|
|
|
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;
|
|
|
|
|
2009-07-15 07:48:37 +08:00
|
|
|
if (feed->pid == pid)
|
2005-04-17 06:20:36 +08:00
|
|
|
dvb_dmx_swfilter_packet_type(feed, buf);
|
2009-07-15 07:48:37 +08:00
|
|
|
else if (feed->pid == 0x2000)
|
2005-04-17 06:20:36 +08:00
|
|
|
feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf,
|
|
|
|
size_t count)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
V4L/DVB (10572): Revert commit dda06a8e4610757def753ee3a541a0b1a1feb36b
On Mon, 02 Feb 2009, Hartmut wrote:
This change set is wrong. The affected functions cannot be called from
an interrupt context, because they may process large buffers. In this
case, interrupts are disabled for a long time. Functions, like
dvb_dmx_swfilter_packets(), could be called only from a tasklet.
This change set does hide some strong design bugs in dm1105.c and
au0828-dvb.c.
Please revert this change set and do fix the bugs in dm1105.c and
au0828-dvb.c (and other files).
On Sun, 15 Feb 2009, Oliver Endriss wrote:
This changeset _must_ be reverted! It breaks all kernels since 2.6.27
for applications which use DVB and require a low interrupt latency.
It is a very bad idea to call the demuxer to process data buffers with
interrupts disabled!
On Mon, 16 Feb 2009, Trent Piepho wrote:
I agree, this is bad. The demuxer is far too much work to be done with
IRQs off. IMHO, even doing it under a spin-lock is excessive. It should
be a mutex. Drivers should use a work-queue to feed the demuxer.
Thank you for testing this changeset and discovering the issues on it.
Cc: Trent Piepho <xyzzy@speakeasy.org>
Cc: Hartmut <e9hack@googlemail.com>
Cc: Oliver Endriss <o.endriss@gmx.de>
Cc: Andreas Oberritter <obi@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2009-02-17 02:27:44 +08:00
|
|
|
spin_lock(&demux->lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
while (count--) {
|
2005-09-10 04:02:26 +08:00
|
|
|
if (buf[0] == 0x47)
|
|
|
|
dvb_dmx_swfilter_packet(demux, buf);
|
2005-04-17 06:20:36 +08:00
|
|
|
buf += 188;
|
|
|
|
}
|
|
|
|
|
V4L/DVB (10572): Revert commit dda06a8e4610757def753ee3a541a0b1a1feb36b
On Mon, 02 Feb 2009, Hartmut wrote:
This change set is wrong. The affected functions cannot be called from
an interrupt context, because they may process large buffers. In this
case, interrupts are disabled for a long time. Functions, like
dvb_dmx_swfilter_packets(), could be called only from a tasklet.
This change set does hide some strong design bugs in dm1105.c and
au0828-dvb.c.
Please revert this change set and do fix the bugs in dm1105.c and
au0828-dvb.c (and other files).
On Sun, 15 Feb 2009, Oliver Endriss wrote:
This changeset _must_ be reverted! It breaks all kernels since 2.6.27
for applications which use DVB and require a low interrupt latency.
It is a very bad idea to call the demuxer to process data buffers with
interrupts disabled!
On Mon, 16 Feb 2009, Trent Piepho wrote:
I agree, this is bad. The demuxer is far too much work to be done with
IRQs off. IMHO, even doing it under a spin-lock is excessive. It should
be a mutex. Drivers should use a work-queue to feed the demuxer.
Thank you for testing this changeset and discovering the issues on it.
Cc: Trent Piepho <xyzzy@speakeasy.org>
Cc: Hartmut <e9hack@googlemail.com>
Cc: Oliver Endriss <o.endriss@gmx.de>
Cc: Andreas Oberritter <obi@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2009-02-17 02:27:44 +08:00
|
|
|
spin_unlock(&demux->lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
EXPORT_SYMBOL(dvb_dmx_swfilter_packets);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-04-08 23:40:51 +08:00
|
|
|
static inline int find_next_packet(const u8 *buf, int pos, size_t count,
|
|
|
|
const int pktsize)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-04-08 23:40:51 +08:00
|
|
|
int start = pos, lost;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-04-08 23:40:51 +08:00
|
|
|
while (pos < count) {
|
|
|
|
if (buf[pos] == 0x47 ||
|
|
|
|
(pktsize == 204 && buf[pos] == 0xB8))
|
|
|
|
break;
|
|
|
|
pos++;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-04-08 23:40:51 +08:00
|
|
|
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;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-04-08 23:40:51 +08:00
|
|
|
return pos;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2005-09-10 04:02:26 +08:00
|
|
|
|
2011-04-08 23:40:51 +08:00
|
|
|
/* 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)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
int p = 0, i, j;
|
2011-04-08 23:40:51 +08:00
|
|
|
const u8 *q;
|
2005-09-10 04:02:26 +08:00
|
|
|
|
V4L/DVB (10572): Revert commit dda06a8e4610757def753ee3a541a0b1a1feb36b
On Mon, 02 Feb 2009, Hartmut wrote:
This change set is wrong. The affected functions cannot be called from
an interrupt context, because they may process large buffers. In this
case, interrupts are disabled for a long time. Functions, like
dvb_dmx_swfilter_packets(), could be called only from a tasklet.
This change set does hide some strong design bugs in dm1105.c and
au0828-dvb.c.
Please revert this change set and do fix the bugs in dm1105.c and
au0828-dvb.c (and other files).
On Sun, 15 Feb 2009, Oliver Endriss wrote:
This changeset _must_ be reverted! It breaks all kernels since 2.6.27
for applications which use DVB and require a low interrupt latency.
It is a very bad idea to call the demuxer to process data buffers with
interrupts disabled!
On Mon, 16 Feb 2009, Trent Piepho wrote:
I agree, this is bad. The demuxer is far too much work to be done with
IRQs off. IMHO, even doing it under a spin-lock is excessive. It should
be a mutex. Drivers should use a work-queue to feed the demuxer.
Thank you for testing this changeset and discovering the issues on it.
Cc: Trent Piepho <xyzzy@speakeasy.org>
Cc: Hartmut <e9hack@googlemail.com>
Cc: Oliver Endriss <o.endriss@gmx.de>
Cc: Andreas Oberritter <obi@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2009-02-17 02:27:44 +08:00
|
|
|
spin_lock(&demux->lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-04-08 23:40:51 +08:00
|
|
|
if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */
|
2005-09-10 04:02:26 +08:00
|
|
|
i = demux->tsbufp;
|
2011-04-08 23:40:51 +08:00
|
|
|
j = pktsize - i;
|
2005-09-10 04:02:26 +08:00
|
|
|
if (count < j) {
|
2005-04-17 06:20:36 +08:00
|
|
|
memcpy(&demux->tsbuf[i], buf, count);
|
|
|
|
demux->tsbufp += count;
|
|
|
|
goto bailout;
|
|
|
|
}
|
|
|
|
memcpy(&demux->tsbuf[i], buf, j);
|
2011-04-08 23:40:51 +08:00
|
|
|
if (demux->tsbuf[0] == 0x47) /* double check */
|
|
|
|
dvb_dmx_swfilter_packet(demux, demux->tsbuf);
|
2005-04-17 06:20:36 +08:00
|
|
|
demux->tsbufp = 0;
|
|
|
|
p += j;
|
|
|
|
}
|
|
|
|
|
2011-04-08 23:40:51 +08:00
|
|
|
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;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2011-04-08 23:40:51 +08:00
|
|
|
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;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bailout:
|
V4L/DVB (10572): Revert commit dda06a8e4610757def753ee3a541a0b1a1feb36b
On Mon, 02 Feb 2009, Hartmut wrote:
This change set is wrong. The affected functions cannot be called from
an interrupt context, because they may process large buffers. In this
case, interrupts are disabled for a long time. Functions, like
dvb_dmx_swfilter_packets(), could be called only from a tasklet.
This change set does hide some strong design bugs in dm1105.c and
au0828-dvb.c.
Please revert this change set and do fix the bugs in dm1105.c and
au0828-dvb.c (and other files).
On Sun, 15 Feb 2009, Oliver Endriss wrote:
This changeset _must_ be reverted! It breaks all kernels since 2.6.27
for applications which use DVB and require a low interrupt latency.
It is a very bad idea to call the demuxer to process data buffers with
interrupts disabled!
On Mon, 16 Feb 2009, Trent Piepho wrote:
I agree, this is bad. The demuxer is far too much work to be done with
IRQs off. IMHO, even doing it under a spin-lock is excessive. It should
be a mutex. Drivers should use a work-queue to feed the demuxer.
Thank you for testing this changeset and discovering the issues on it.
Cc: Trent Piepho <xyzzy@speakeasy.org>
Cc: Hartmut <e9hack@googlemail.com>
Cc: Oliver Endriss <o.endriss@gmx.de>
Cc: Andreas Oberritter <obi@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2009-02-17 02:27:44 +08:00
|
|
|
spin_unlock(&demux->lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-04-08 23:40:51 +08:00
|
|
|
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);
|
|
|
|
}
|
2005-09-10 04:02:26 +08:00
|
|
|
EXPORT_SYMBOL(dvb_dmx_swfilter_204);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-08-28 05:46:37 +08:00
|
|
|
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);
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
for (i = 0; i < demux->filternum; i++)
|
2005-04-17 06:20:36 +08:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static struct dvb_demux_feed *dvb_dmx_feed_alloc(struct dvb_demux *demux)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
for (i = 0; i < demux->feednum; i++)
|
2005-04-17 06:20:36 +08:00
|
|
|
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",
|
2008-04-09 10:20:00 +08:00
|
|
|
__func__, feed->type, feed->state, feed->pid);
|
2005-04-17 06:20:36 +08:00
|
|
|
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",
|
2008-04-09 10:20:00 +08:00
|
|
|
__func__, feed->type, feed->state, feed->pid);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_del(&feed->list_head);
|
|
|
|
out:
|
|
|
|
spin_unlock_irq(&feed->demux->lock);
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
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)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct dvb_demux *demux = feed->demux;
|
|
|
|
|
|
|
|
if (pid > DMX_MAX_PID)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2006-02-07 16:49:14 +08:00
|
|
|
if (mutex_lock_interruptible(&demux->mutex))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
|
|
|
if (ts_type & TS_DECODER) {
|
|
|
|
if (pes_type >= DMX_TS_PES_OTHER) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (demux->pesfilter[pes_type] &&
|
|
|
|
demux->pesfilter[pes_type] != feed) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
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
|
2005-09-10 04:02:26 +08:00
|
|
|
feed->buffer = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
#else
|
|
|
|
feed->buffer = vmalloc(feed->buffer_size);
|
|
|
|
if (!feed->buffer) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
feed->state = DMX_STATE_READY;
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static int dmx_ts_feed_start_filtering(struct dmx_ts_feed *ts_feed)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct dvb_demux *demux = feed->demux;
|
|
|
|
int ret;
|
|
|
|
|
2006-02-07 16:49:14 +08:00
|
|
|
if (mutex_lock_interruptible(&demux->mutex))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
|
|
|
if (feed->state != DMX_STATE_READY || feed->type != DMX_TYPE_TS) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!demux->start_feed) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = demux->start_feed(feed)) < 0) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_irq(&demux->lock);
|
|
|
|
ts_feed->is_filtering = 1;
|
|
|
|
feed->state = DMX_STATE_GO;
|
|
|
|
spin_unlock_irq(&demux->lock);
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static int dmx_ts_feed_stop_filtering(struct dmx_ts_feed *ts_feed)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct dvb_demux *demux = feed->demux;
|
|
|
|
int ret;
|
|
|
|
|
2007-03-10 17:21:25 +08:00
|
|
|
mutex_lock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (feed->state < DMX_STATE_GO) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!demux->stop_feed) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
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);
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,
|
|
|
|
struct dmx_ts_feed **ts_feed,
|
|
|
|
dmx_ts_cb callback)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux *demux = (struct dvb_demux *)dmx;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct dvb_demux_feed *feed;
|
|
|
|
|
2006-02-07 16:49:14 +08:00
|
|
|
if (mutex_lock_interruptible(&demux->mutex))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
|
|
|
if (!(feed = dvb_dmx_feed_alloc(demux))) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
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;
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
feed->filter->type = DMX_TYPE_TS;
|
|
|
|
feed->filter->feed = feed;
|
|
|
|
feed->filter->state = DMX_STATE_READY;
|
|
|
|
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static int dvbdmx_release_ts_feed(struct dmx_demux *dmx,
|
|
|
|
struct dmx_ts_feed *ts_feed)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux *demux = (struct dvb_demux *)dmx;
|
|
|
|
struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-03-10 17:21:25 +08:00
|
|
|
mutex_lock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (feed->state == DMX_STATE_FREE) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
#ifndef NOBUFS
|
|
|
|
vfree(feed->buffer);
|
2005-09-10 04:02:26 +08:00
|
|
|
feed->buffer = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
#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;
|
|
|
|
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&demux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* dmx_section_feed API calls
|
|
|
|
******************************************************************************/
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static int dmx_section_feed_allocate_filter(struct dmx_section_feed *feed,
|
|
|
|
struct dmx_section_filter **filter)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct dvb_demux *dvbdemux = dvbdmxfeed->demux;
|
|
|
|
struct dvb_demux_filter *dvbdmxfilter;
|
|
|
|
|
2006-02-07 16:49:14 +08:00
|
|
|
if (mutex_lock_interruptible(&dvbdemux->mutex))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
|
|
|
dvbdmxfilter = dvb_dmx_filter_alloc(dvbdemux);
|
|
|
|
if (!dvbdmxfilter) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdemux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
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);
|
|
|
|
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdemux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static int dmx_section_feed_set(struct dmx_section_feed *feed,
|
|
|
|
u16 pid, size_t circular_buffer_size,
|
|
|
|
int check_crc)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
|
|
|
|
|
|
|
|
if (pid > 0x1fff)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2006-02-07 16:49:14 +08:00
|
|
|
if (mutex_lock_interruptible(&dvbdmx->mutex))
|
2005-04-17 06:20:36 +08:00
|
|
|
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
|
2005-09-10 04:02:26 +08:00
|
|
|
dvbdmxfeed->buffer = vmalloc(dvbdmxfeed->buffer_size);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!dvbdmxfeed->buffer) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
dvbdmxfeed->state = DMX_STATE_READY;
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
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;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
if (!(f = dvbdmxfeed->filter))
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
do {
|
|
|
|
sf = &f->filter;
|
|
|
|
doneq = 0;
|
2005-09-10 04:02:26 +08:00
|
|
|
for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {
|
2005-04-17 06:20:36 +08:00
|
|
|
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)
|
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
|
|
|
|
int ret;
|
|
|
|
|
2006-02-07 16:49:14 +08:00
|
|
|
if (mutex_lock_interruptible(&dvbdmx->mutex))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
|
|
|
if (feed->is_filtering) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dvbdmxfeed->filter) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
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) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
prepare_secfilters(dvbdmxfeed);
|
|
|
|
|
|
|
|
if ((ret = dvbdmx->start_feed(dvbdmxfeed)) < 0) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_irq(&dvbdmx->lock);
|
|
|
|
feed->is_filtering = 1;
|
|
|
|
dvbdmxfeed->state = DMX_STATE_GO;
|
|
|
|
spin_unlock_irq(&dvbdmx->lock);
|
|
|
|
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static int dmx_section_feed_stop_filtering(struct dmx_section_feed *feed)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
|
|
|
|
int ret;
|
|
|
|
|
2007-03-10 17:21:25 +08:00
|
|
|
mutex_lock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!dvbdmx->stop_feed) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
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);
|
|
|
|
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dmx_section_feed_release_filter(struct dmx_section_feed *feed,
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dmx_section_filter *filter)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux_filter *dvbdmxfilter = (struct dvb_demux_filter *)filter, *f;
|
|
|
|
struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
|
|
|
|
|
2007-03-10 17:21:25 +08:00
|
|
|
mutex_lock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (dvbdmxfilter->feed != dvbdmxfeed) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
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 {
|
2005-09-10 04:02:26 +08:00
|
|
|
while (f->next != dvbdmxfilter)
|
2005-04-17 06:20:36 +08:00
|
|
|
f = f->next;
|
|
|
|
f->next = f->next->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
dvbdmxfilter->state = DMX_STATE_FREE;
|
|
|
|
spin_unlock_irq(&dvbdmx->lock);
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dvbdmx_allocate_section_feed(struct dmx_demux *demux,
|
|
|
|
struct dmx_section_feed **feed,
|
|
|
|
dmx_section_cb callback)
|
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct dvb_demux_feed *dvbdmxfeed;
|
|
|
|
|
2006-02-07 16:49:14 +08:00
|
|
|
if (mutex_lock_interruptible(&dvbdmx->mutex))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
|
|
|
if (!(dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx))) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
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;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
(*feed) = &dvbdmxfeed->feed.sec;
|
2005-04-17 06:20:36 +08:00
|
|
|
(*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;
|
|
|
|
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dvbdmx_release_section_feed(struct dmx_demux *demux,
|
|
|
|
struct dmx_section_feed *feed)
|
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
|
|
|
|
struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-03-10 17:21:25 +08:00
|
|
|
mutex_lock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
if (dvbdmxfeed->state == DMX_STATE_FREE) {
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
#ifndef NOBUFS
|
|
|
|
vfree(dvbdmxfeed->buffer);
|
2005-09-10 04:02:26 +08:00
|
|
|
dvbdmxfeed->buffer = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
2005-09-10 04:02:26 +08:00
|
|
|
dvbdmxfeed->state = DMX_STATE_FREE;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
dvb_demux_feed_del(dvbdmxfeed);
|
|
|
|
|
|
|
|
dvbdmxfeed->pid = 0xffff;
|
|
|
|
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdmx->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* dvb_demux kernel data API calls
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
|
|
static int dvbdmx_open(struct dmx_demux *demux)
|
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (dvbdemux->users >= MAX_DVB_DEMUX_USERS)
|
|
|
|
return -EUSERS;
|
|
|
|
|
|
|
|
dvbdemux->users++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dvbdmx_close(struct dmx_demux *demux)
|
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (dvbdemux->users == 0)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
dvbdemux->users--;
|
|
|
|
//FIXME: release any unneeded resources if users==0
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-06-23 01:20:19 +08:00
|
|
|
static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t count)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
|
2008-06-23 01:20:19 +08:00
|
|
|
void *p;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2010-05-22 16:21:02 +08:00
|
|
|
p = memdup_user(buf, count);
|
|
|
|
if (IS_ERR(p))
|
|
|
|
return PTR_ERR(p);
|
2008-06-23 01:20:19 +08:00
|
|
|
if (mutex_lock_interruptible(&dvbdemux->mutex)) {
|
|
|
|
kfree(p);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ERESTARTSYS;
|
2008-06-23 01:20:19 +08:00
|
|
|
}
|
|
|
|
dvb_dmx_swfilter(dvbdemux, p, count);
|
|
|
|
kfree(p);
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdemux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (signal_pending(current))
|
|
|
|
return -EINTR;
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static int dvbdmx_add_frontend(struct dmx_demux *demux,
|
|
|
|
struct dmx_frontend *frontend)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct list_head *head = &dvbdemux->frontend_list;
|
|
|
|
|
|
|
|
list_add(&(frontend->connectivity_list), head);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static int dvbdmx_remove_frontend(struct dmx_demux *demux,
|
|
|
|
struct dmx_frontend *frontend)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct list_head *pos, *n, *head = &dvbdemux->frontend_list;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
list_for_each_safe(pos, n, head) {
|
2005-04-17 06:20:36 +08:00
|
|
|
if (DMX_FE_ENTRY(pos) == frontend) {
|
|
|
|
list_del(pos);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static struct list_head *dvbdmx_get_frontends(struct dmx_demux *demux)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (list_empty(&dvbdemux->frontend_list))
|
|
|
|
return NULL;
|
2005-09-10 04:02:26 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return &dvbdemux->frontend_list;
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static int dvbdmx_connect_frontend(struct dmx_demux *demux,
|
|
|
|
struct dmx_frontend *frontend)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (demux->frontend)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2007-03-10 17:21:25 +08:00
|
|
|
mutex_lock(&dvbdemux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
demux->frontend = frontend;
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdemux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dvbdmx_disconnect_frontend(struct dmx_demux *demux)
|
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-03-10 17:21:25 +08:00
|
|
|
mutex_lock(&dvbdemux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
demux->frontend = NULL;
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_unlock(&dvbdemux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 * pids)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-10 04:02:26 +08:00
|
|
|
struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
memcpy(pids, dvbdemux->pids, 5 * sizeof(u16));
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int dvb_dmx_init(struct dvb_demux *dvbdemux)
|
|
|
|
{
|
2005-09-10 04:02:23 +08:00
|
|
|
int i;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct dmx_demux *dmx = &dvbdemux->dmx;
|
|
|
|
|
2009-06-06 20:31:34 +08:00
|
|
|
dvbdemux->cnt_storage = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
dvbdemux->users = 0;
|
2005-09-10 04:02:26 +08:00
|
|
|
dvbdemux->filter = vmalloc(dvbdemux->filternum * sizeof(struct dvb_demux_filter));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!dvbdemux->filter)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
dvbdemux->feed = vmalloc(dvbdemux->feednum * sizeof(struct dvb_demux_feed));
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!dvbdemux->feed) {
|
|
|
|
vfree(dvbdemux->filter);
|
2010-02-01 21:35:22 +08:00
|
|
|
dvbdemux->filter = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2005-09-10 04:02:26 +08:00
|
|
|
for (i = 0; i < dvbdemux->filternum; i++) {
|
2005-04-17 06:20:36 +08:00
|
|
|
dvbdemux->filter[i].state = DMX_STATE_FREE;
|
|
|
|
dvbdemux->filter[i].index = i;
|
|
|
|
}
|
2005-09-10 04:02:26 +08:00
|
|
|
for (i = 0; i < dvbdemux->feednum; i++) {
|
2005-04-17 06:20:36 +08:00
|
|
|
dvbdemux->feed[i].state = DMX_STATE_FREE;
|
|
|
|
dvbdemux->feed[i].index = i;
|
|
|
|
}
|
2005-09-10 04:02:26 +08:00
|
|
|
|
2010-03-07 01:58:01 +08:00
|
|
|
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");
|
2010-02-01 22:50:42 +08:00
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
INIT_LIST_HEAD(&dvbdemux->frontend_list);
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
for (i = 0; i < DMX_TS_PES_OTHER; i++) {
|
2005-04-17 06:20:36 +08:00
|
|
|
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;
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
if (!dvbdemux->memcopy)
|
|
|
|
dvbdemux->memcopy = dvb_dmx_memcopy;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
dmx->frontend = NULL;
|
2005-09-10 04:02:26 +08:00
|
|
|
dmx->priv = dvbdemux;
|
2005-04-17 06:20:36 +08:00
|
|
|
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;
|
|
|
|
|
2006-02-07 16:49:14 +08:00
|
|
|
mutex_init(&dvbdemux->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_lock_init(&dvbdemux->lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-10 04:02:26 +08:00
|
|
|
EXPORT_SYMBOL(dvb_dmx_init);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-09-10 04:02:23 +08:00
|
|
|
void dvb_dmx_release(struct dvb_demux *dvbdemux)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-06-06 20:31:34 +08:00
|
|
|
vfree(dvbdemux->cnt_storage);
|
2005-04-17 06:20:36 +08:00
|
|
|
vfree(dvbdemux->filter);
|
|
|
|
vfree(dvbdemux->feed);
|
|
|
|
}
|
2005-09-10 04:02:26 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
EXPORT_SYMBOL(dvb_dmx_release);
|