dvb_demux.c 30.9 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
#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>
34
#include <linux/uaccess.h>
35
#include <asm/div64.h>
Linus Torvalds's avatar
Linus Torvalds committed
36 37 38

#include "dvb_demux.h"

39 40 41 42 43
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");

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

49 50 51 52 53
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)");

54 55 56 57 58 59 60
#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)
61

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

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

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

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

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

Linus Torvalds's avatar
Linus Torvalds committed
88 89 90
	return 184;
}

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

96 97
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
98
{
99
	memcpy(d, s, len);
Linus Torvalds's avatar
Linus Torvalds committed
100 101 102 103 104 105
}

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

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

	if (count == 0)
		return -1;

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

121
#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG
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");
127
#endif
Linus Torvalds's avatar
Linus Torvalds committed
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
static int dmx_ts_feed_set(struct dmx_ts_feed *ts_feed, u16 pid, int ts_type,
664
			   enum dmx_ts_pes pes_type, ktime_t timeout)
Linus Torvalds's avatar
Linus Torvalds committed
665
{
666
	struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
Linus Torvalds's avatar
Linus Torvalds committed
667 668 669 670 671
	struct dvb_demux *demux = feed->demux;

	if (pid > DMX_MAX_PID)
		return -EINVAL;

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

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

		if (demux->pesfilter[pes_type] &&
		    demux->pesfilter[pes_type] != feed) {
683
			mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
			return -EINVAL;
		}

		demux->pesfilter[pes_type] = feed;
		demux->pids[pes_type] = pid;
	}

	dvb_demux_feed_add(feed);

	feed->pid = pid;
	feed->timeout = timeout;
	feed->ts_type = ts_type;
	feed->pes_type = pes_type;

	feed->state = DMX_STATE_READY;
699
	mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
700 701 702 703

	return 0;
}

704
static int dmx_ts_feed_start_filtering(struct dmx_ts_feed *ts_feed)
Linus Torvalds's avatar
Linus Torvalds committed
705
{
706
	struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
Linus Torvalds's avatar
Linus Torvalds committed
707 708 709
	struct dvb_demux *demux = feed->demux;
	int ret;

710
	if (mutex_lock_interruptible(&demux->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
711 712 713
		return -ERESTARTSYS;

	if (feed->state != DMX_STATE_READY || feed->type != DMX_TYPE_TS) {
714
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
715 716 717 718
		return -EINVAL;
	}

	if (!demux->start_feed) {
719
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
720 721 722 723
		return -ENODEV;
	}

	if ((ret = demux->start_feed(feed)) < 0) {
724
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
725 726 727 728 729 730 731
		return ret;
	}

	spin_lock_irq(&demux->lock);
	ts_feed->is_filtering = 1;
	feed->state = DMX_STATE_GO;
	spin_unlock_irq(&demux->lock);
732
	mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
733 734 735 736

	return 0;
}

737
static int dmx_ts_feed_stop_filtering(struct dmx_ts_feed *ts_feed)
Linus Torvalds's avatar
Linus Torvalds committed
738
{
739
	struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
Linus Torvalds's avatar
Linus Torvalds committed
740 741 742
	struct dvb_demux *demux = feed->demux;
	int ret;

743
	mutex_lock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
744 745

	if (feed->state < DMX_STATE_GO) {
746
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
747 748 749 750
		return -EINVAL;
	}

	if (!demux->stop_feed) {
751
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
752 753 754 755 756 757 758 759 760
		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);
761
	mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
762 763 764 765

	return ret;
}

766 767 768
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
769
{
770
	struct dvb_demux *demux = (struct dvb_demux *)dmx;
Linus Torvalds's avatar
Linus Torvalds committed
771 772
	struct dvb_demux_feed *feed;

773
	if (mutex_lock_interruptible(&demux->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
774 775 776
		return -ERESTARTSYS;

	if (!(feed = dvb_dmx_feed_alloc(demux))) {
777
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
		return -EBUSY;
	}

	feed->type = DMX_TYPE_TS;
	feed->cb.ts = callback;
	feed->demux = demux;
	feed->pid = 0xffff;
	feed->peslen = 0xfffa;

	(*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;
797
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
798 799 800 801 802 803 804
		return -EBUSY;
	}

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

805
	mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
806 807 808 809

	return 0;
}

810 811
static int dvbdmx_release_ts_feed(struct dmx_demux *dmx,
				  struct dmx_ts_feed *ts_feed)
Linus Torvalds's avatar
Linus Torvalds committed
812
{
813 814
	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
815

816
	mutex_lock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
817 818

	if (feed->state == DMX_STATE_FREE) {
819
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
820 821 822 823 824 825 826 827 828 829
		return -EINVAL;
	}

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

	dvb_demux_feed_del(feed);

	feed->pid = 0xffff;

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

833
	mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
834 835 836 837 838 839 840
	return 0;
}

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

841 842
static int dmx_section_feed_allocate_filter(struct dmx_section_feed *feed,
					    struct dmx_section_filter **filter)
Linus Torvalds's avatar
Linus Torvalds committed
843
{
844
	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
Linus Torvalds's avatar
Linus Torvalds committed
845 846 847
	struct dvb_demux *dvbdemux = dvbdmxfeed->demux;
	struct dvb_demux_filter *dvbdmxfilter;

848
	if (mutex_lock_interruptible(&dvbdemux->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
849 850 851 852
		return -ERESTARTSYS;

	dvbdmxfilter = dvb_dmx_filter_alloc(dvbdemux);
	if (!dvbdmxfilter) {
853
		mutex_unlock(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
854 855 856 857 858 859 860 861 862 863 864 865 866 867
		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);

868
	mutex_unlock(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
869 870 871
	return 0;
}

872
static int dmx_section_feed_set(struct dmx_section_feed *feed,
873
				u16 pid, int check_crc)
Linus Torvalds's avatar
Linus Torvalds committed
874
{
875
	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
Linus Torvalds's avatar
Linus Torvalds committed
876 877 878 879 880
	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;

	if (pid > 0x1fff)
		return -EINVAL;

881
	if (mutex_lock_interruptible(&dvbdmx->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
882 883 884 885 886 887 888 889
		return -ERESTARTSYS;

	dvb_demux_feed_add(dvbdmxfeed);

	dvbdmxfeed->pid = pid;
	dvbdmxfeed->feed.sec.check_crc = check_crc;

	dvbdmxfeed->state = DMX_STATE_READY;
890
	mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
891 892 893 894 895 896 897 898 899 900
	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;

901
	if (!(f = dvbdmxfeed->filter))
Linus Torvalds's avatar
Linus Torvalds committed
902 903 904 905
		return;
	do {
		sf = &f->filter;
		doneq = 0;
906
		for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
907 908 909 910 911 912 913 914 915 916 917
			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)
{
918
	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
Linus Torvalds's avatar
Linus Torvalds committed
919 920 921
	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
	int ret;

922
	if (mutex_lock_interruptible(&dvbdmx->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
923 924 925
		return -ERESTARTSYS;

	if (feed->is_filtering) {
926
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
927 928 929 930
		return -EBUSY;
	}

	if (!dvbdmxfeed->filter) {
931
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
932 933 934 935 936 937 938 939 940
		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) {
941
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
942 943 944 945 946 947
		return -ENODEV;
	}

	prepare_secfilters(dvbdmxfeed);

	if ((ret = dvbdmx->start_feed(dvbdmxfeed)) < 0) {
948
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
949 950 951 952 953 954 955 956
		return ret;
	}

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

957
	mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
958 959 960
	return 0;
}

961
static int dmx_section_feed_stop_filtering(struct dmx_section_feed *feed)
Linus Torvalds's avatar
Linus Torvalds committed
962
{
963
	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
Linus Torvalds's avatar
Linus Torvalds committed
964 965 966
	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
	int ret;

967
	mutex_lock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
968 969

	if (!dvbdmx->stop_feed) {
970
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
971 972 973 974 975 976 977 978 979 980
		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);

981
	mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
982 983 984 985
	return ret;
}

static int dmx_section_feed_release_filter(struct dmx_section_feed *feed,
986
					   struct dmx_section_filter *filter)
Linus Torvalds's avatar
Linus Torvalds committed
987
{
988 989
	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
990 991
	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;

992
	mutex_lock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
993 994

	if (dvbdmxfilter->feed != dvbdmxfeed) {
995
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
996 997 998
		return -EINVAL;
	}

999
	if (feed->is_filtering) {
1000 1001
		/* release dvbdmx->mutex as far as it is
		   acquired by stop_filtering() itself */
1002
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1003
		feed->stop_filtering(feed);
1004 1005
		mutex_lock(&dvbdmx->mutex);
	}
Linus Torvalds's avatar
Linus Torvalds committed
1006 1007 1008 1009 1010 1011 1012

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

	if (f == dvbdmxfilter) {
		dvbdmxfeed->filter = dvbdmxfilter->next;
	} else {
1013
		while (f->next != dvbdmxfilter)
Linus Torvalds's avatar
Linus Torvalds committed
1014 1015 1016 1017 1018 1019
			f = f->next;
		f->next = f->next->next;
	}

	dvbdmxfilter->state = DMX_STATE_FREE;
	spin_unlock_irq(&dvbdmx->lock);
1020
	mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1021 1022 1023 1024 1025 1026 1027
	return 0;
}

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

1031
	if (mutex_lock_interruptible(&dvbdmx->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
1032 1033 1034
		return -ERESTARTSYS;

	if (!(dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx))) {
1035
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
		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;

1048
	(*feed) = &dvbdmxfeed->feed.sec;
Linus Torvalds's avatar
Linus Torvalds committed
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
	(*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;

1059
	mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1060 1061 1062 1063 1064 1065
	return 0;
}

static int dvbdmx_release_section_feed(struct dmx_demux *demux,
				       struct dmx_section_feed *feed)
{
1066 1067
	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
1068

1069
	mutex_lock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1070

1071
	if (dvbdmxfeed->state == DMX_STATE_FREE) {
1072
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1073 1074
		return -EINVAL;
	}
1075
	dvbdmxfeed->state = DMX_STATE_FREE;
Linus Torvalds's avatar
Linus Torvalds committed
1076 1077 1078 1079 1080

	dvb_demux_feed_del(dvbdmxfeed);

	dvbdmxfeed->pid = 0xffff;

1081
	mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1082 1083 1084 1085 1086 1087 1088 1089 1090
	return 0;
}

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

static int dvbdmx_open(struct dmx_demux *demux)
{
1091
	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
Linus Torvalds's avatar
Linus Torvalds committed
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101

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

	dvbdemux->users++;
	return 0;
}

static int dvbdmx_close(struct dmx_demux *demux)
{
1102
	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
Linus Torvalds's avatar
Linus Torvalds committed
1103 1104 1105 1106 1107 1108 1109 1110 1111

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

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

1112
static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t count)
Linus Torvalds's avatar
Linus Torvalds committed
1113
{
1114
	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
1115
	void *p;
Linus Torvalds's avatar
Linus Torvalds committed
1116 1117 1118 1119

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

1120 1121 1122
	p = memdup_user(buf, count);
	if (IS_ERR(p))
		return PTR_ERR(p);
1123 1124
	if (mutex_lock_interruptible(&dvbdemux->mutex)) {
		kfree(p);
Linus Torvalds's avatar
Linus Torvalds committed
1125
		return -ERESTARTSYS;
1126 1127 1128
	}
	dvb_dmx_swfilter(dvbdemux, p, count);
	kfree(p);
1129
	mutex_unlock(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1130 1131 1132 1133 1134 1135

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

1136 1137
static int dvbdmx_add_frontend(struct dmx_demux *demux,
			       struct dmx_frontend *frontend)
Linus Torvalds's avatar
Linus Torvalds committed
1138
{
1139
	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
Linus Torvalds's avatar
Linus Torvalds committed
1140 1141 1142 1143 1144 1145 1146
	struct list_head *head = &dvbdemux->frontend_list;

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

	return 0;
}

1147 1148
static int dvbdmx_remove_frontend(struct dmx_demux *demux,
				  struct dmx_frontend *frontend)
Linus Torvalds's avatar
Linus Torvalds committed
1149
{
1150
	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
Linus Torvalds's avatar
Linus Torvalds committed
1151 1152
	struct list_head *pos, *n, *head = &dvbdemux->frontend_list;

1153
	list_for_each_safe(pos, n, head) {
Linus Torvalds's avatar
Linus Torvalds committed
1154 1155 1156 1157 1158 1159 1160 1161 1162
		if (DMX_FE_ENTRY(pos) == frontend) {
			list_del(pos);
			return 0;
		}
	}

	return -ENODEV;
}

1163
static struct list_head *dvbdmx_get_frontends(struct dmx_demux *demux)
Linus Torvalds's avatar
Linus Torvalds committed
1164
{
1165
	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
Linus Torvalds's avatar
Linus Torvalds committed
1166 1167 1168

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

Linus Torvalds's avatar
Linus Torvalds committed
1170 1171 1172
	return &dvbdemux->frontend_list;
}

1173 1174
static int dvbdmx_connect_frontend(struct dmx_demux *demux,
				   struct dmx_frontend *frontend)
Linus Torvalds's avatar
Linus Torvalds committed
1175
{
1176
	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
Linus Torvalds's avatar
Linus Torvalds committed
1177 1178 1179 1180

	if (demux->frontend)
		return -EINVAL;

1181
	mutex_lock(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1182 1183

	demux->frontend = frontend;
1184
	mutex_unlock(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1185 1186 1187 1188 1189
	return 0;
}

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

1192
	mutex_lock(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1193 1194

	demux->frontend = NULL;
1195
	mutex_unlock(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1196 1197 1198
	return 0;
}

1199
static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 * pids)
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
	memcpy(pids, dvbdemux->pids, 5 * sizeof(u16));
Linus Torvalds's avatar
Linus Torvalds committed
1204 1205 1206 1207 1208
	return 0;
}

int dvb_dmx_init(struct dvb_demux *dvbdemux)
{
1209
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
1210 1211
	struct dmx_demux *dmx = &dvbdemux->dmx;

1212
	dvbdemux->cnt_storage = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
1213
	dvbdemux->users = 0;
1214
	dvbdemux->filter = vmalloc(dvbdemux->filternum * sizeof(struct dvb_demux_filter));
Linus Torvalds's avatar
Linus Torvalds committed
1215 1216 1217 1218

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

1219
	dvbdemux->feed = vmalloc(dvbdemux->feednum * sizeof(struct dvb_demux_feed));
Linus Torvalds's avatar
Linus Torvalds committed
1220 1221
	if (!dvbdemux->feed) {
		vfree(dvbdemux->filter);
1222
		dvbdemux->filter = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
1223 1224
		return -ENOMEM;
	}
1225
	for (i = 0; i < dvbdemux->filternum; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
1226 1227 1228
		dvbdemux->filter[i].state = DMX_STATE_FREE;
		dvbdemux->filter[i].index = i;
	}
1229
	for (i = 0; i < dvbdemux->feednum; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
1230 1231 1232
		dvbdemux->feed[i].state = DMX_STATE_FREE;
		dvbdemux->feed[i].index = i;
	}
1233

1234 1235
	dvbdemux->cnt_storage = vmalloc(MAX_PID + 1);
	if (!dvbdemux->cnt_storage)
1236
		pr_warn("Couldn't allocate memory for TS/TEI check. Disabling it\n");
1237

1238 1239
	INIT_LIST_HEAD(&dvbdemux->frontend_list);

1240
	for (i = 0; i < DMX_PES_OTHER; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253
		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;

1254 1255
	if (!dvbdemux->memcopy)
		dvbdemux->memcopy = dvb_dmx_memcopy;
Linus Torvalds's avatar
Linus Torvalds committed
1256 1257

	dmx->frontend = NULL;
1258
	dmx->priv = dvbdemux;
Linus Torvalds's avatar
Linus Torvalds committed
1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273
	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;

1274
	mutex_init(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1275 1276 1277 1278 1279
	spin_lock_init(&dvbdemux->lock);

	return 0;
}

1280
EXPORT_SYMBOL(dvb_dmx_init);
Linus Torvalds's avatar
Linus Torvalds committed
1281

1282
void dvb_dmx_release(struct dvb_demux *dvbdemux)
Linus Torvalds's avatar
Linus Torvalds committed
1283
{
1284
	vfree(dvbdemux->cnt_storage);
Linus Torvalds's avatar
Linus Torvalds committed