dvb_demux.c 31.6 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * 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.
 *
 */

24 25
#define pr_fmt(fmt) "dvb_demux: " fmt

26
#include <linux/sched.h>
Linus Torvalds's avatar
Linus Torvalds committed
27 28 29 30 31 32 33 34
#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>
35
#include <asm/div64.h>
Linus Torvalds's avatar
Linus Torvalds committed
36 37 38 39 40

#include "dvb_demux.h"

#define NOBUFS

41 42 43 44 45
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");

46 47 48 49 50
static int dvb_demux_speedcheck;
module_param(dvb_demux_speedcheck, int, 0644);
MODULE_PARM_DESC(dvb_demux_speedcheck,
		"enable transport stream speed check");

51 52 53 54 55
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)");

56 57 58 59 60 61 62
#define dprintk(fmt, arg...) \
	printk(KERN_DEBUG pr_fmt("%s: " fmt),  __func__, ##arg)

#define dprintk_tscheck(x...) do {			\
	if (dvb_demux_tscheck && printk_ratelimit())	\
		dprintk(x);				\
} while (0)
63

Linus Torvalds's avatar
Linus Torvalds committed
64 65 66 67 68 69
/******************************************************************************
 * static inlined helper functions
 ******************************************************************************/

static inline u16 section_length(const u8 *buf)
{
70
	return 3 + ((buf[1] & 0x0f) << 8) + buf[2];
Linus Torvalds's avatar
Linus Torvalds committed
71 72 73 74
}

static inline u16 ts_pid(const u8 *buf)
{
75
	return ((buf[1] & 0x1f) << 8) + buf[2];
Linus Torvalds's avatar
Linus Torvalds committed
76 77 78 79
}

static inline u8 payload(const u8 *tsp)
{
80
	if (!(tsp[3] & 0x10))	// no payload?
Linus Torvalds's avatar
Linus Torvalds committed
81
		return 0;
82 83 84

	if (tsp[3] & 0x20) {	// adaptation field?
		if (tsp[4] > 183)	// corrupted data?
Linus Torvalds's avatar
Linus Torvalds committed
85 86
			return 0;
		else
87
			return 184 - 1 - tsp[4];
Linus Torvalds's avatar
Linus Torvalds committed
88
	}
89

Linus Torvalds's avatar
Linus Torvalds committed
90 91 92
	return 184;
}

93
static u32 dvb_dmx_crc32(struct dvb_demux_feed *f, const u8 *src, size_t len)
Linus Torvalds's avatar
Linus Torvalds committed
94
{
95
	return (f->feed.sec.crc_val = crc32_be(f->feed.sec.crc_val, src, len));
Linus Torvalds's avatar
Linus Torvalds committed
96 97
}

98 99
static void dvb_dmx_memcopy(struct dvb_demux_feed *f, u8 *d, const u8 *s,
			    size_t len)
Linus Torvalds's avatar
Linus Torvalds committed
100
{
101
	memcpy(d, s, len);
Linus Torvalds's avatar
Linus Torvalds committed
102 103 104 105 106 107
}

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

108 109
static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed,
					   const u8 *buf)
Linus Torvalds's avatar
Linus Torvalds committed
110 111 112 113 114 115 116 117 118
{
	int count = payload(buf);
	int p;
	//int ccok;
	//u8 cc;

	if (count == 0)
		return -1;

119
	p = 188 - count;
Linus Torvalds's avatar
Linus Torvalds committed
120 121

	/*
122 123 124
	cc = buf[3] & 0x0f;
	ccok = ((feed->cc + 1) & 0x0f) == cc;
	feed->cc = cc;
Linus Torvalds's avatar
Linus Torvalds committed
125
	if (!ccok)
126
		dprintk("missed packet!\n");
Linus Torvalds's avatar
Linus Torvalds committed
127 128
	*/

129
	if (buf[1] & 0x40)	// PUSI ?
Linus Torvalds's avatar
Linus Torvalds committed
130 131 132 133
		feed->peslen = 0xfffa;

	feed->peslen += count;

134
	return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts);
Linus Torvalds's avatar
Linus Torvalds committed
135 136
}

137 138
static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed,
					  struct dvb_demux_filter *f)
Linus Torvalds's avatar
Linus Torvalds committed
139 140 141 142
{
	u8 neq = 0;
	int i;

143
	for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
144 145 146 147 148 149 150 151 152 153 154
		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;

155
	return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen,
156
			    NULL, 0, &f->filter);
Linus Torvalds's avatar
Linus Torvalds committed
157 158
}

159
static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed)
Linus Torvalds's avatar
Linus Torvalds committed
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
{
	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;

193
#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG
194
	if (sec->secbufp < sec->tsfeedp) {
Linus Torvalds's avatar
Linus Torvalds committed
195 196
		int i, n = sec->tsfeedp - sec->secbufp;

197 198 199 200 201 202
		/*
		 * 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) {
203
			dprintk("dvb_demux.c section ts padding loss: %d/%d\n",
Linus Torvalds's avatar
Linus Torvalds committed
204
			       n, sec->tsfeedp);
205
			dprintk("dvb_demux.c pad data:");
206
			for (i = 0; i < n; i++)
207 208
				pr_cont(" %02x", sec->secbuf[i]);
			pr_cont("\n");
Linus Torvalds's avatar
Linus Torvalds committed
209 210 211 212 213 214 215 216 217
		}
	}
#endif

	sec->tsfeedp = sec->secbufp = sec->seclen = 0;
	sec->secbuf = sec->secbuf_base;
}

/*
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
 * 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)
Linus Torvalds's avatar
Linus Torvalds committed
237 238 239 240 241
{
	struct dvb_demux *demux = feed->demux;
	struct dmx_section_feed *sec = &feed->feed.sec;
	u16 limit, seclen, n;

242
	if (sec->tsfeedp >= DMX_MAX_SECFEED_SIZE)
Linus Torvalds's avatar
Linus Torvalds committed
243 244
		return 0;

245
	if (sec->tsfeedp + len > DMX_MAX_SECFEED_SIZE) {
246
#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG
247
		dprintk("dvb_demux.c section buffer full loss: %d/%d\n",
248 249
		       sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE,
		       DMX_MAX_SECFEED_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
250 251 252 253
#endif
		len = DMX_MAX_SECFEED_SIZE - sec->tsfeedp;
	}

254
	if (len <= 0)
Linus Torvalds's avatar
Linus Torvalds committed
255 256 257 258 259
		return 0;

	demux->memcopy(feed, sec->secbuf_base + sec->tsfeedp, buf, len);
	sec->tsfeedp += len;

260 261 262
	/*
	 * Dump all the sections we can find in the data (Emard)
	 */
Linus Torvalds's avatar
Linus Torvalds committed
263
	limit = sec->tsfeedp;
264 265
	if (limit > DMX_MAX_SECFEED_SIZE)
		return -1;	/* internal error should never happen */
Linus Torvalds's avatar
Linus Torvalds committed
266 267 268 269

	/* to be sure always set secbuf */
	sec->secbuf = sec->secbuf_base + sec->secbufp;

270
	for (n = 0; sec->secbufp + 2 < limit; n++) {
Linus Torvalds's avatar
Linus Torvalds committed
271
		seclen = section_length(sec->secbuf);
272
		if (seclen <= 0 || seclen > DMX_MAX_SECTION_SIZE
273
		    || seclen + sec->secbufp > limit)
Linus Torvalds's avatar
Linus Torvalds committed
274 275 276 277
			return 0;
		sec->seclen = seclen;
		sec->crc_val = ~0;
		/* dump [secbuf .. secbuf+seclen) */
278
		if (feed->pusi_seen)
Linus Torvalds's avatar
Linus Torvalds committed
279
			dvb_dmx_swfilter_section_feed(feed);
280
#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG
Linus Torvalds's avatar
Linus Torvalds committed
281
		else
282
			dprintk("dvb_demux.c pusi not seen, discarding section data\n");
Linus Torvalds's avatar
Linus Torvalds committed
283
#endif
284 285
		sec->secbufp += seclen;	/* secbufp and secbuf moving together is */
		sec->secbuf += seclen;	/* redundant but saves pointer arithmetic */
Linus Torvalds's avatar
Linus Torvalds committed
286 287 288 289 290
	}

	return 0;
}

291 292
static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,
					   const u8 *buf)
Linus Torvalds's avatar
Linus Torvalds committed
293 294 295 296 297 298 299
{
	u8 p, count;
	int ccok, dc_i = 0;
	u8 cc;

	count = payload(buf);

300
	if (count == 0)		/* count == 0 if no payload or out of range */
Linus Torvalds's avatar
Linus Torvalds committed
301 302
		return -1;

303
	p = 188 - count;	/* payload start */
Linus Torvalds's avatar
Linus Torvalds committed
304 305 306 307 308 309 310 311 312 313 314 315

	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) {
316
#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG
317 318
		dprintk("dvb_demux.c discontinuity detected %d bytes lost\n",
			count);
319 320 321 322
		/*
		 * those bytes under sume circumstances will again be reported
		 * in the following dvb_dmx_swfilter_section_new
		 */
Linus Torvalds's avatar
Linus Torvalds committed
323
#endif
324 325 326 327
		/*
		 * Discontinuity detected. Reset pusi_seen = 0 to
		 * stop feeding of suspicious data until next PUSI=1 arrives
		 */
Linus Torvalds's avatar
Linus Torvalds committed
328 329 330 331 332
		feed->pusi_seen = 0;
		dvb_dmx_swfilter_section_new(feed);
	}

	if (buf[1] & 0x40) {
333
		/* PUSI=1 (is set), section boundary is here */
Linus Torvalds's avatar
Linus Torvalds committed
334
		if (count > 1 && buf[p] < count) {
335
			const u8 *before = &buf[p + 1];
Linus Torvalds's avatar
Linus Torvalds committed
336
			u8 before_len = buf[p];
337 338
			const u8 *after = &before[before_len];
			u8 after_len = count - 1 - before_len;
Linus Torvalds's avatar
Linus Torvalds committed
339

340 341
			dvb_dmx_swfilter_section_copy_dump(feed, before,
							   before_len);
Linus Torvalds's avatar
Linus Torvalds committed
342 343 344
			/* before start of new section, set pusi_seen = 1 */
			feed->pusi_seen = 1;
			dvb_dmx_swfilter_section_new(feed);
345 346
			dvb_dmx_swfilter_section_copy_dump(feed, after,
							   after_len);
Linus Torvalds's avatar
Linus Torvalds committed
347
		}
348
#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG
349
		else if (count > 0)
350 351
			dprintk("dvb_demux.c PUSI=1 but %d bytes lost\n",
				count);
Linus Torvalds's avatar
Linus Torvalds committed
352 353
#endif
	} else {
354 355
		/* PUSI=0 (is not set), no section boundary */
		dvb_dmx_swfilter_section_copy_dump(feed, &buf[p], count);
Linus Torvalds's avatar
Linus Torvalds committed
356
	}
357

Linus Torvalds's avatar
Linus Torvalds committed
358 359 360
	return 0;
}

361 362
static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed,
						const u8 *buf)
Linus Torvalds's avatar
Linus Torvalds committed
363
{
364
	switch (feed->type) {
Linus Torvalds's avatar
Linus Torvalds committed
365 366 367 368 369 370 371
	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
372
				feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts);
Linus Torvalds's avatar
Linus Torvalds committed
373 374 375 376 377 378 379 380 381 382
		}
		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)
383
			feed->feed.sec.seclen = feed->feed.sec.secbufp = 0;
Linus Torvalds's avatar
Linus Torvalds committed
384 385 386 387 388 389 390 391 392 393
		break;

	default:
		break;
	}
}

#define DVR_FEED(f)							\
	(((f)->type == DMX_TYPE_TS) &&					\
	((f)->feed.ts.is_filtering) &&					\
394
	(((f)->ts_type & (TS_PACKET | TS_DEMUX)) == TS_PACKET))
Linus Torvalds's avatar
Linus Torvalds committed
395 396 397 398 399 400 401

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;

402
	if (dvb_demux_speedcheck) {
403
		ktime_t cur_time;
404 405 406 407 408 409
		u64 speed_bytes, speed_timedelta;

		demux->speed_pkts_cnt++;

		/* show speed every SPEED_PKTS_INTERVAL packets */
		if (!(demux->speed_pkts_cnt % SPEED_PKTS_INTERVAL)) {
410
			cur_time = ktime_get();
411

412
			if (ktime_to_ns(demux->speed_last_time) != 0) {
413 414 415 416 417
				speed_bytes = (u64)demux->speed_pkts_cnt
					* 188 * 8;
				/* convert to 1024 basis */
				speed_bytes = 1000 * div64_u64(speed_bytes,
						1024);
418 419
				speed_timedelta = ktime_ms_delta(cur_time,
							demux->speed_last_time);
420 421 422
				dprintk("TS speed %llu Kbits/sec \n",
					div64_u64(speed_bytes,
						  speed_timedelta));
423
			}
424 425 426

			demux->speed_last_time = cur_time;
			demux->speed_pkts_cnt = 0;
427 428
		}
	}
429

430
	if (buf[1] & 0x80) {
431
		dprintk_tscheck("TEI detected. PID=0x%x data1=0x%x\n",
432
				pid, buf[1]);
433
		/* data in this packet can't be trusted - drop it unless
434 435 436 437
		 * 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 */
438 439 440 441 442 443 444 445 446
		if (demux->cnt_storage && dvb_demux_tscheck) {
			/* check pkt counter */
			if (pid < MAX_PID) {
				if (buf[3] & 0x10)
					demux->cnt_storage[pid] =
						(demux->cnt_storage[pid] + 1) & 0xf;

				if ((buf[3] & 0xf) != demux->cnt_storage[pid]) {
					dprintk_tscheck("TS packet counter mismatch. PID=0x%x expected 0x%x got 0x%x\n",
447 448
						pid, demux->cnt_storage[pid],
						buf[3] & 0xf);
449 450 451 452
					demux->cnt_storage[pid] = buf[3] & 0xf;
				}
			}
			/* end check */
453
		}
454

455
	list_for_each_entry(feed, &demux->feed_list, list_head) {
Linus Torvalds's avatar
Linus Torvalds committed
456 457 458 459 460 461 462 463
		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;

464
		if (feed->pid == pid)
Linus Torvalds's avatar
Linus Torvalds committed
465
			dvb_dmx_swfilter_packet_type(feed, buf);
466
		else if (feed->pid == 0x2000)
467
			feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts);
Linus Torvalds's avatar
Linus Torvalds committed
468 469 470
	}
}

471 472
void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf,
			      size_t count)
Linus Torvalds's avatar
Linus Torvalds committed
473
{
474 475 476
	unsigned long flags;

	spin_lock_irqsave(&demux->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
477 478

	while (count--) {
479 480
		if (buf[0] == 0x47)
			dvb_dmx_swfilter_packet(demux, buf);
Linus Torvalds's avatar
Linus Torvalds committed
481 482 483
		buf += 188;
	}

484
	spin_unlock_irqrestore(&demux->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
485 486
}

487
EXPORT_SYMBOL(dvb_dmx_swfilter_packets);
Linus Torvalds's avatar
Linus Torvalds committed
488

489 490
static inline int find_next_packet(const u8 *buf, int pos, size_t count,
				   const int pktsize)
Linus Torvalds's avatar
Linus Torvalds committed
491
{
492
	int start = pos, lost;
Linus Torvalds's avatar
Linus Torvalds committed
493

494 495 496 497 498
	while (pos < count) {
		if (buf[pos] == 0x47 ||
		    (pktsize == 204 && buf[pos] == 0xB8))
			break;
		pos++;
Linus Torvalds's avatar
Linus Torvalds committed
499 500
	}

501 502 503 504 505 506 507
	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;
Linus Torvalds's avatar
Linus Torvalds committed
508 509
	}

510
	return pos;
Linus Torvalds's avatar
Linus Torvalds committed
511
}
512

513 514 515
/* 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)
Linus Torvalds's avatar
Linus Torvalds committed
516
{
517
	int p = 0, i, j;
518
	const u8 *q;
519
	unsigned long flags;
520

521
	spin_lock_irqsave(&demux->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
522

523
	if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */
524
		i = demux->tsbufp;
525
		j = pktsize - i;
526
		if (count < j) {
Linus Torvalds's avatar
Linus Torvalds committed
527 528 529 530 531
			memcpy(&demux->tsbuf[i], buf, count);
			demux->tsbufp += count;
			goto bailout;
		}
		memcpy(&demux->tsbuf[i], buf, j);
532 533
		if (demux->tsbuf[0] == 0x47) /* double check */
			dvb_dmx_swfilter_packet(demux, demux->tsbuf);
Linus Torvalds's avatar
Linus Torvalds committed
534 535 536 537
		demux->tsbufp = 0;
		p += j;
	}

538 539 540 541 542 543 544 545 546 547 548 549 550
	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;
Linus Torvalds's avatar
Linus Torvalds committed
551
		}
552 553 554 555 556 557 558 559 560 561
		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;
Linus Torvalds's avatar
Linus Torvalds committed
562 563 564
	}

bailout:
565
	spin_unlock_irqrestore(&demux->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
566 567
}

568 569 570 571 572 573 574 575 576 577
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);
}
578
EXPORT_SYMBOL(dvb_dmx_swfilter_204);
Linus Torvalds's avatar
Linus Torvalds committed
579

580 581
void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count)
{
582 583 584
	unsigned long flags;

	spin_lock_irqsave(&demux->lock, flags);
585

586
	demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts);
587

588
	spin_unlock_irqrestore(&demux->lock, flags);
589 590 591
}
EXPORT_SYMBOL(dvb_dmx_swfilter_raw);

592
static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux)
Linus Torvalds's avatar
Linus Torvalds committed
593 594 595
{
	int i;

596
	for (i = 0; i < demux->filternum; i++)
Linus Torvalds's avatar
Linus Torvalds committed
597 598 599 600 601 602 603 604 605 606 607
		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];
}

608
static struct dvb_demux_feed *dvb_dmx_feed_alloc(struct dvb_demux *demux)
Linus Torvalds's avatar
Linus Torvalds committed
609 610 611
{
	int i;

612
	for (i = 0; i < demux->feednum; i++)
Linus Torvalds's avatar
Linus Torvalds committed
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
		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)) {
639
		pr_err("%s: feed already in list (type=%x state=%x pid=%x)\n",
640
		       __func__, feed->type, feed->state, feed->pid);
Linus Torvalds's avatar
Linus Torvalds committed
641 642 643 644 645 646 647 648 649 650 651 652
		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))) {
653
		pr_err("%s: feed not in list (type=%x state=%x pid=%x)\n",
654
		       __func__, feed->type, feed->state, feed->pid);
Linus Torvalds's avatar
Linus Torvalds committed
655 656 657 658 659 660 661 662
		goto out;
	}

	list_del(&feed->list_head);
out:
	spin_unlock_irq(&feed->demux->lock);
}

663 664
static int dmx_ts_feed_set(struct dmx_ts_feed *ts_feed, u16 pid, int ts_type,
			   enum dmx_ts_pes pes_type,
665
			   size_t circular_buffer_size, ktime_t timeout)
Linus Torvalds's avatar
Linus Torvalds committed
666
{
667
	struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
Linus Torvalds's avatar
Linus Torvalds committed
668 669 670 671 672
	struct dvb_demux *demux = feed->demux;

	if (pid > DMX_MAX_PID)
		return -EINVAL;

673
	if (mutex_lock_interruptible(&demux->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
674 675 676
		return -ERESTARTSYS;

	if (ts_type & TS_DECODER) {
677
		if (pes_type >= DMX_PES_OTHER) {
678
			mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
679 680 681 682 683
			return -EINVAL;
		}

		if (demux->pesfilter[pes_type] &&
		    demux->pesfilter[pes_type] != feed) {
684
			mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
			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
702
		feed->buffer = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
703 704 705
#else
		feed->buffer = vmalloc(feed->buffer_size);
		if (!feed->buffer) {
706
			mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
707 708 709 710 711 712
			return -ENOMEM;
		}
#endif
	}

	feed->state = DMX_STATE_READY;
713
	mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
714 715 716 717

	return 0;
}

718
static int dmx_ts_feed_start_filtering(struct dmx_ts_feed *ts_feed)
Linus Torvalds's avatar
Linus Torvalds committed
719
{
720
	struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
Linus Torvalds's avatar
Linus Torvalds committed
721 722 723
	struct dvb_demux *demux = feed->demux;
	int ret;

724
	if (mutex_lock_interruptible(&demux->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
725 726 727
		return -ERESTARTSYS;

	if (feed->state != DMX_STATE_READY || feed->type != DMX_TYPE_TS) {
728
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
729 730 731 732
		return -EINVAL;
	}

	if (!demux->start_feed) {
733
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
734 735 736 737
		return -ENODEV;
	}

	if ((ret = demux->start_feed(feed)) < 0) {
738
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
739 740 741 742 743 744 745
		return ret;
	}

	spin_lock_irq(&demux->lock);
	ts_feed->is_filtering = 1;
	feed->state = DMX_STATE_GO;
	spin_unlock_irq(&demux->lock);
746
	mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
747 748 749 750

	return 0;
}

751
static int dmx_ts_feed_stop_filtering(struct dmx_ts_feed *ts_feed)
Linus Torvalds's avatar
Linus Torvalds committed
752
{
753
	struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
Linus Torvalds's avatar
Linus Torvalds committed
754 755 756
	struct dvb_demux *demux = feed->demux;
	int ret;

757
	mutex_lock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
758 759

	if (feed->state < DMX_STATE_GO) {
760
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
761 762 763 764
		return -EINVAL;
	}

	if (!demux->stop_feed) {
765
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
766 767 768 769 770 771 772 773 774
		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);
775
	mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
776 777 778 779

	return ret;
}

780 781 782
static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,
				   struct dmx_ts_feed **ts_feed,
				   dmx_ts_cb callback)
Linus Torvalds's avatar
Linus Torvalds committed
783
{
784
	struct dvb_demux *demux = (struct dvb_demux *)dmx;
Linus Torvalds's avatar
Linus Torvalds committed
785 786
	struct dvb_demux_feed *feed;

787
	if (mutex_lock_interruptible(&demux->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
788 789 790
		return -ERESTARTSYS;

	if (!(feed = dvb_dmx_feed_alloc(demux))) {
791
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
		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;
812
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
813 814 815 816 817 818 819
		return -EBUSY;
	}

	feed->filter->type = DMX_TYPE_TS;
	feed->filter->feed = feed;
	feed->filter->state = DMX_STATE_READY;

820
	mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
821 822 823 824

	return 0;
}

825 826
static int dvbdmx_release_ts_feed(struct dmx_demux *dmx,
				  struct dmx_ts_feed *ts_feed)
Linus Torvalds's avatar
Linus Torvalds committed
827
{
828 829
	struct dvb_demux *demux = (struct dvb_demux *)dmx;
	struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
Linus Torvalds's avatar
Linus Torvalds committed
830

831
	mutex_lock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
832 833

	if (feed->state == DMX_STATE_FREE) {
834
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
835 836 837 838
		return -EINVAL;
	}
#ifndef NOBUFS
	vfree(feed->buffer);
839
	feed->buffer = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
840 841 842 843 844 845 846 847 848
#endif

	feed->state = DMX_STATE_FREE;
	feed->filter->state = DMX_STATE_FREE;

	dvb_demux_feed_del(feed);

	feed->pid = 0xffff;

849
	if (feed->ts_type & TS_DECODER && feed->pes_type < DMX_PES_OTHER)
Linus Torvalds's avatar
Linus Torvalds committed
850 851
		demux->pesfilter[feed->pes_type] = NULL;

852
	mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
853 854 855 856 857 858 859
	return 0;
}

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

860 861
static int dmx_section_feed_allocate_filter(struct dmx_section_feed *feed,
					    struct dmx_section_filter **filter)
Linus Torvalds's avatar
Linus Torvalds committed
862
{
863
	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
Linus Torvalds's avatar
Linus Torvalds committed
864 865 866
	struct dvb_demux *dvbdemux = dvbdmxfeed->demux;
	struct dvb_demux_filter *dvbdmxfilter;

867
	if (mutex_lock_interruptible(&dvbdemux->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
868 869 870 871
		return -ERESTARTSYS;

	dvbdmxfilter = dvb_dmx_filter_alloc(dvbdemux);
	if (!dvbdmxfilter) {
872
		mutex_unlock(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
873 874 875 876 877 878 879 880 881 882 883 884 885 886
		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);

887
	mutex_unlock(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
888 889 890
	return 0;
}

891 892 893
static int dmx_section_feed_set(struct dmx_section_feed *feed,
				u16 pid, size_t circular_buffer_size,
				int check_crc)
Linus Torvalds's avatar
Linus Torvalds committed
894
{
895
	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
Linus Torvalds's avatar
Linus Torvalds committed
896 897 898 899 900
	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;

	if (pid > 0x1fff)
		return -EINVAL;

901
	if (mutex_lock_interruptible(&dvbdmx->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
902 903 904 905 906 907 908 909 910 911 912
		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
913
	dvbdmxfeed->buffer = vmalloc(dvbdmxfeed->buffer_size);
Linus Torvalds's avatar
Linus Torvalds committed
914
	if (!dvbdmxfeed->buffer) {
915
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
916 917 918 919 920
		return -ENOMEM;
	}
#endif

	dvbdmxfeed->state = DMX_STATE_READY;
921
	mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
922 923 924 925 926 927 928 929 930 931
	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;

932
	if (!(f = dvbdmxfeed->filter))
Linus Torvalds's avatar
Linus Torvalds committed
933 934 935 936
		return;
	do {
		sf = &f->filter;
		doneq = 0;
937
		for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
938 939 940 941 942 943 944 945 946 947 948
			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)
{
949
	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
Linus Torvalds's avatar
Linus Torvalds committed
950 951 952
	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
	int ret;

953
	if (mutex_lock_interruptible(&dvbdmx->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
954 955 956
		return -ERESTARTSYS;

	if (feed->is_filtering) {
957
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
958 959 960 961
		return -EBUSY;
	}

	if (!dvbdmxfeed->filter) {
962
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
963 964 965 966 967 968 969 970 971
		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) {
972
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
973 974 975 976 977 978
		return -ENODEV;
	}

	prepare_secfilters(dvbdmxfeed);

	if ((ret = dvbdmx->start_feed(dvbdmxfeed)) < 0) {
979
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
980 981 982 983 984 985 986 987
		return ret;
	}

	spin_lock_irq(&dvbdmx->lock);
	feed->is_filtering = 1;
	dvbdmxfeed->state = DMX_STATE_GO;
	spin_unlock_irq(&dvbdmx->lock);

988
	mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
989 990 991
	return 0;
}

992
static int dmx_section_feed_stop_filtering(struct dmx_section_feed *feed)
Linus Torvalds's avatar
Linus Torvalds committed
993
{
994
	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
Linus Torvalds's avatar
Linus Torvalds committed
995 996 997
	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
	int ret;

998
	mutex_lock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
999 1000

	if (!dvbdmx->stop_feed) {
1001
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
		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);

1012
	mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1013 1014 1015 1016
	return ret;
}

static int dmx_section_feed_release_filter(struct dmx_section_feed *feed,
1017
					   struct dmx_section_filter *filter)
Linus Torvalds's avatar
Linus Torvalds committed
1018
{
1019 1020
	struct dvb_demux_filter *dvbdmxfilter = (struct dvb_demux_filter *)filter, *f;
	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
Linus Torvalds's avatar
Linus Torvalds committed
1021 1022
	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;

1023
	mutex_lock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1024 1025

	if (dvbdmxfilter->feed != dvbdmxfeed) {
1026
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1027 1028 1029
		return -EINVAL;
	}

1030
	if (feed->is_filtering) {
1031 1032
		/* release dvbdmx->mutex as far as it is
		   acquired by stop_filtering() itself */
1033
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1034
		feed->stop_filtering(feed);
1035 1036
		mutex_lock(&dvbdmx->mutex);
	}
Linus Torvalds's avatar
Linus Torvalds committed
1037 1038 1039 1040 1041 1042 1043

	spin_lock_irq(&dvbdmx->lock);
	f = dvbdmxfeed->filter;

	if (f == dvbdmxfilter) {
		dvbdmxfeed->filter = dvbdmxfilter->next;
	} else {
1044
		while (f->next != dvbdmxfilter)
Linus Torvalds's avatar
Linus Torvalds committed
1045 1046 1047 1048 1049 1050
			f = f->next;
		f->next = f->next->next;
	}

	dvbdmxfilter->state = DMX_STATE_FREE;
	spin_unlock_irq(&dvbdmx->lock);
1051
	mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1052 1053 1054 1055 1056 1057 1058
	return 0;
}

static int dvbdmx_allocate_section_feed(struct dmx_demux *demux,
					struct dmx_section_feed **feed,
					dmx_section_cb callback)
{
1059
	struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;
Linus Torvalds's avatar
Linus Torvalds committed
1060 1061
	struct dvb_demux_feed *dvbdmxfeed;

1062
	if (mutex_lock_interruptible(&dvbdmx->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
1063 1064 1065
		return -ERESTARTSYS;

	if (!(dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx))) {
1066
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079
		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;

1080
	(*feed) = &dvbdmxfeed->feed.sec;
Linus Torvalds's avatar
Linus Torvalds committed
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090
	(*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;

1091
	mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1092 1093 1094 1095 1096 1097
	return 0;
}

static int dvbdmx_release_section_feed(struct dmx_demux *demux,
				       struct dmx_section_feed *feed)
{
1098 1099
	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
	struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;
Linus Torvalds's avatar
Linus Torvalds committed
1100

1101
	mutex_lock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1102

1103
	if (dvbdmxfeed->state == DMX_STATE_FREE) {
1104
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1105 1106 1107 1108
		return -EINVAL;
	}
#ifndef NOBUFS
	vfree(dvbdmxfeed->buffer);
1109
	dvbdmxfeed->buffer = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
1110
#endif
1111
	dvbdmxfeed->state = DMX_STATE_FREE;
Linus Torvalds's avatar
Linus Torvalds committed
1112 1113 1114 1115 1116

	dvb_demux_feed_del(dvbdmxfeed);

	dvbdmxfeed->pid = 0xffff;

1117
	mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1118 1119 1120 1121 1122 1123 1124 1125 1126
	return 0;
}

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

static int dvbdmx_open(struct dmx_demux *demux)
{
1127
	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
Linus Torvalds's avatar
Linus Torvalds committed
1128 1129 1130 1131 1132 1133 1134 1135 1136 1137

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

	dvbdemux->users++;
	return 0;
}

static int dvbdmx_close(struct dmx_demux *demux)
{
1138
	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
Linus Torvalds's avatar
Linus Torvalds committed
1139 1140 1141 1142 1143 1144 1145 1146 1147

	if (dvbdemux->users == 0)
		return -ENODEV;

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

1148
static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t count)
Linus Torvalds's avatar
Linus Torvalds committed
1149
{
1150
	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
1151
	void *p;
Linus Torvalds's avatar
Linus Torvalds committed
1152 1153 1154 1155

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

1156 1157 1158
	p = memdup_user(buf, count);
	if (IS_ERR(p))
		return PTR_ERR(p);
1159 1160
	if (mutex_lock_interruptible(&dvbdemux->mutex)) {
		kfree(p);
Linus Torvalds's avatar
Linus Torvalds committed
1161
		return -ERESTARTSYS;
1162 1163 1164
	}
	dvb_dmx_swfilter(dvbdemux, p, count);
	kfree(p);
1165
	mutex_unlock(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1166 1167 1168 1169 1170 1171

	if (signal_pending(current))
		return -EINTR;
	return count;
}

1172 1173
static int dvbdmx_add_frontend(struct dmx_demux *demux,
			       struct dmx_frontend *frontend)
Linus Torvalds's avatar
Linus Torvalds committed
1174
{
1175
	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
Linus Torvalds's avatar
Linus Torvalds committed
1176 1177 1178 1179 1180 1181 1182
	struct list_head *head = &dvbdemux->frontend_list;

	list_add(&(frontend->connectivity_list), head);

	return 0;
}

1183 1184
static int dvbdmx_remove_frontend(struct dmx_demux *demux,
				  struct dmx_frontend *frontend)
Linus Torvalds's avatar
Linus Torvalds committed
1185
{
1186
	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
Linus Torvalds's avatar
Linus Torvalds committed
1187 1188
	struct list_head *pos, *n, *head = &dvbdemux->frontend_list;

1189
	list_for_each_safe(pos, n, head) {
Linus Torvalds's avatar
Linus Torvalds committed
1190 1191 1192 1193 1194 1195 1196 1197 1198
		if (DMX_FE_ENTRY(pos) == frontend) {
			list_del(pos);
			return 0;
		}
	}

	return -ENODEV;
}

1199
static struct list_head *dvbdmx_get_frontends(struct dmx_demux *demux)
Linus Torvalds's avatar
Linus Torvalds committed
1200
{
1201
	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
Linus Torvalds's avatar
Linus Torvalds committed
1202 1203 1204

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

Linus Torvalds's avatar
Linus Torvalds committed
1206 1207 1208
	return &dvbdemux->frontend_list;
}

1209 1210
static int dvbdmx_connect_frontend(struct dmx_demux *demux,
				   struct dmx_frontend *frontend)
Linus Torvalds's avatar
Linus Torvalds committed
1211
{
1212
	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
Linus Torvalds's avatar
Linus Torvalds committed
1213 1214 1215 1216

	if (demux->frontend)
		return -EINVAL;

1217
	mutex_lock(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1218 1219

	demux->frontend = frontend;
1220
	mutex_unlock(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1221 1222 1223 1224 1225
	return 0;
}

static int dvbdmx_disconnect_frontend(struct dmx_demux *demux)
{
1226
	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
Linus Torvalds's avatar
Linus Torvalds committed
1227

1228
	mutex_lock(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1229 1230

	demux->frontend = NULL;
1231
	mutex_unlock(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1232 1233 1234
	return 0;
}

1235
static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 * pids)
Linus Torvalds's avatar
Linus Torvalds committed
1236
{
1237
	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
Linus Torvalds's avatar
Linus Torvalds committed
1238

1239
	memcpy(pids, dvbdemux->pids, 5 * sizeof(u16));
Linus Torvalds's avatar
Linus Torvalds committed
1240 1241 1242 1243 1244
	return 0;
}

int dvb_dmx_init(struct dvb_demux *dvbdemux)
{
1245
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
1246 1247
	struct dmx_demux *dmx = &dvbdemux->dmx;

1248
	dvbdemux->cnt_storage = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
1249
	dvbdemux->users = 0;
1250
	dvbdemux->filter = vmalloc(dvbdemux->filternum * sizeof(struct dvb_demux_filter));
Linus Torvalds's avatar
Linus Torvalds committed
1251 1252 1253 1254

	if (!dvbdemux->filter)
		return -ENOMEM;

1255
	dvbdemux->feed = vmalloc(dvbdemux->feednum * sizeof(struct dvb_demux_feed));
Linus Torvalds's avatar
Linus Torvalds committed
1256 1257
	if (!dvbdemux->feed) {
		vfree(dvbdemux->filter);
1258
		dvbdemux->filter = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
1259 1260
		return -ENOMEM;
	}
1261
	for (i = 0; i < dvbdemux->filternum; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
1262 1263 1264
		dvbdemux->filter[i].state = DMX_STATE_FREE;
		dvbdemux->filter[i].index = i;
	}
1265
	for (i = 0; i < dvbdemux->feednum; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
1266 1267 1268
		dvbdemux->feed[i].state = DMX_STATE_FREE;
		dvbdemux->feed[i].index = i;
	}
1269

1270 1271
	dvbdemux->cnt_storage = vmalloc(MAX_PID + 1);
	if (!dvbdemux->cnt_storage)
1272
		pr_warn("Couldn't allocate memory for TS/TEI check. Disabling it\n");
1273

1274 1275
	INIT_LIST_HEAD(&dvbdemux->frontend_list);

1276
	for (i = 0; i < DMX_PES_OTHER; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289
		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;

1290 1291
	if (!dvbdemux->memcopy)
		dvbdemux->memcopy = dvb_dmx_memcopy;
Linus Torvalds's avatar
Linus Torvalds committed
1292 1293

	dmx->frontend = NULL;
1294
	dmx->priv = dvbdemux;