dvb_demux.c 32.1 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
/*
 * 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.
 *
 */

20 21
#define pr_fmt(fmt) "dvb_demux: " fmt

22
#include <linux/sched/signal.h>
Linus Torvalds's avatar
Linus Torvalds committed
23 24 25 26 27 28 29
#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>
30
#include <linux/uaccess.h>
31
#include <asm/div64.h>
Linus Torvalds's avatar
Linus Torvalds committed
32

33
#include <media/dvb_demux.h>
Linus Torvalds's avatar
Linus Torvalds committed
34

35 36 37 38 39
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");

40 41 42 43 44
static int dvb_demux_speedcheck;
module_param(dvb_demux_speedcheck, int, 0644);
MODULE_PARM_DESC(dvb_demux_speedcheck,
		"enable transport stream speed check");

45 46 47 48 49
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)");

50 51 52 53 54 55 56
#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)
57

58 59 60 61 62 63 64 65 66 67 68
#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG
#  define dprintk_sect_loss(x...) dprintk(x)
#else
#  define dprintk_sect_loss(x...)
#endif

#define set_buf_flags(__feed, __flag)			\
	do {						\
		(__feed)->buffer_flags |= (__flag);	\
	} while (0)

Linus Torvalds's avatar
Linus Torvalds committed
69 70 71 72 73 74
/******************************************************************************
 * static inlined helper functions
 ******************************************************************************/

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

static inline u16 ts_pid(const u8 *buf)
{
80
	return ((buf[1] & 0x1f) << 8) + buf[2];
Linus Torvalds's avatar
Linus Torvalds committed
81 82 83 84
}

static inline u8 payload(const u8 *tsp)
{
85
	if (!(tsp[3] & 0x10))	// no payload?
Linus Torvalds's avatar
Linus Torvalds committed
86
		return 0;
87 88 89

	if (tsp[3] & 0x20) {	// adaptation field?
		if (tsp[4] > 183)	// corrupted data?
Linus Torvalds's avatar
Linus Torvalds committed
90 91
			return 0;
		else
92
			return 184 - 1 - tsp[4];
Linus Torvalds's avatar
Linus Torvalds committed
93
	}
94

Linus Torvalds's avatar
Linus Torvalds committed
95 96 97
	return 184;
}

98
static u32 dvb_dmx_crc32(struct dvb_demux_feed *f, const u8 *src, size_t len)
Linus Torvalds's avatar
Linus Torvalds committed
99
{
100
	return (f->feed.sec.crc_val = crc32_be(f->feed.sec.crc_val, src, len));
Linus Torvalds's avatar
Linus Torvalds committed
101 102
}

103 104
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
105
{
106
	memcpy(d, s, len);
Linus Torvalds's avatar
Linus Torvalds committed
107 108 109 110 111 112
}

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

113 114
static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed,
					   const u8 *buf)
Linus Torvalds's avatar
Linus Torvalds committed
115 116 117
{
	int count = payload(buf);
	int p;
118 119
	int ccok;
	u8 cc;
Linus Torvalds's avatar
Linus Torvalds committed
120 121 122 123

	if (count == 0)
		return -1;

124
	p = 188 - count;
Linus Torvalds's avatar
Linus Torvalds committed
125

126 127 128
	cc = buf[3] & 0x0f;
	ccok = ((feed->cc + 1) & 0x0f) == cc;
	feed->cc = cc;
129 130 131 132 133
	if (!ccok) {
		set_buf_flags(feed, DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
		dprintk_sect_loss("missed packet: %d instead of %d!\n",
				  cc, (feed->cc + 1) & 0x0f);
	}
Linus Torvalds's avatar
Linus Torvalds committed
134

135
	if (buf[1] & 0x40)	// PUSI ?
Linus Torvalds's avatar
Linus Torvalds committed
136 137 138 139
		feed->peslen = 0xfffa;

	feed->peslen += count;

140 141
	return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts,
			   &feed->buffer_flags);
Linus Torvalds's avatar
Linus Torvalds committed
142 143
}

144 145
static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed,
					  struct dvb_demux_filter *f)
Linus Torvalds's avatar
Linus Torvalds committed
146 147 148 149
{
	u8 neq = 0;
	int i;

150
	for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
151 152 153 154 155 156 157 158 159 160 161
		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;

162
	return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen,
163
			    NULL, 0, &f->filter, &feed->buffer_flags);
Linus Torvalds's avatar
Linus Torvalds committed
164 165
}

166
static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed)
Linus Torvalds's avatar
Linus Torvalds committed
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
{
	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 &&
182 183
		    demux->check_crc32(feed, sec->secbuf, sec->seclen)) {
			set_buf_flags(feed, DMX_BUFFER_FLAG_HAD_CRC32_DISCARD);
Linus Torvalds's avatar
Linus Torvalds committed
184
			return -1;
185
		}
Linus Torvalds's avatar
Linus Torvalds committed
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
	}

	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;

202
	if (sec->secbufp < sec->tsfeedp) {
203
		int n = sec->tsfeedp - sec->secbufp;
Linus Torvalds's avatar
Linus Torvalds committed
204

205 206 207 208 209 210
		/*
		 * 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) {
211 212 213 214 215
			set_buf_flags(feed,
				      DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
			dprintk_sect_loss("section ts padding loss: %d/%d\n",
					  n, sec->tsfeedp);
			dprintk_sect_loss("pad data: %*ph\n", n, sec->secbuf);
Linus Torvalds's avatar
Linus Torvalds committed
216 217 218 219 220 221 222 223
		}
	}

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

/*
224 225 226 227 228 229 230 231 232 233 234 235
 * 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:
236
 * when demux is started, let feed->pusi_seen = false to
237 238
 * prevent initial feeding of garbage from the end of
 * previous section. When you for the first time see PUSI=1
239
 * then set feed->pusi_seen = true
240 241 242
 */
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
243 244 245 246 247
{
	struct dvb_demux *demux = feed->demux;
	struct dmx_section_feed *sec = &feed->feed.sec;
	u16 limit, seclen, n;

248
	if (sec->tsfeedp >= DMX_MAX_SECFEED_SIZE)
Linus Torvalds's avatar
Linus Torvalds committed
249 250
		return 0;

251
	if (sec->tsfeedp + len > DMX_MAX_SECFEED_SIZE) {
252 253 254 255
		set_buf_flags(feed, DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
		dprintk_sect_loss("section buffer full loss: %d/%d\n",
				  sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE,
				  DMX_MAX_SECFEED_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
256 257 258
		len = DMX_MAX_SECFEED_SIZE - sec->tsfeedp;
	}

259
	if (len <= 0)
Linus Torvalds's avatar
Linus Torvalds committed
260 261 262 263 264
		return 0;

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

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

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

275
	for (n = 0; sec->secbufp + 2 < limit; n++) {
Linus Torvalds's avatar
Linus Torvalds committed
276
		seclen = section_length(sec->secbuf);
277
		if (seclen <= 0 || seclen > DMX_MAX_SECTION_SIZE
278
		    || seclen + sec->secbufp > limit)
Linus Torvalds's avatar
Linus Torvalds committed
279 280 281 282
			return 0;
		sec->seclen = seclen;
		sec->crc_val = ~0;
		/* dump [secbuf .. secbuf+seclen) */
283
		if (feed->pusi_seen) {
Linus Torvalds's avatar
Linus Torvalds committed
284
			dvb_dmx_swfilter_section_feed(feed);
285 286 287 288 289
		} else {
			set_buf_flags(feed,
				      DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
			dprintk_sect_loss("pusi not seen, discarding section data\n");
		}
290 291
		sec->secbufp += seclen;	/* secbufp and secbuf moving together is */
		sec->secbuf += seclen;	/* redundant but saves pointer arithmetic */
Linus Torvalds's avatar
Linus Torvalds committed
292 293 294 295 296
	}

	return 0;
}

297 298
static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,
					   const u8 *buf)
Linus Torvalds's avatar
Linus Torvalds committed
299 300 301 302 303 304 305
{
	u8 p, count;
	int ccok, dc_i = 0;
	u8 cc;

	count = payload(buf);

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

309
	p = 188 - count;	/* payload start */
Linus Torvalds's avatar
Linus Torvalds committed
310 311 312 313 314 315 316 317 318 319 320 321

	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) {
322 323 324 325
		if (dc_i) {
			set_buf_flags(feed,
				      DMX_BUFFER_FLAG_DISCONTINUITY_INDICATOR);
			dprintk_sect_loss("%d frame with disconnect indicator\n",
326
				cc);
327 328 329 330
		} else {
			set_buf_flags(feed,
				      DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
			dprintk_sect_loss("discontinuity: %d instead of %d. %d bytes lost\n",
331
				cc, (feed->cc + 1) & 0x0f, count + 4);
332
		}
333
		/*
334
		 * those bytes under some circumstances will again be reported
335 336
		 * in the following dvb_dmx_swfilter_section_new
		 */
337

338
		/*
339
		 * Discontinuity detected. Reset pusi_seen to
340
		 * stop feeding of suspicious data until next PUSI=1 arrives
341 342 343
		 *
		 * FIXME: does it make sense if the MPEG-TS is the one
		 *	reporting discontinuity?
344
		 */
345

346
		feed->pusi_seen = false;
Linus Torvalds's avatar
Linus Torvalds committed
347 348 349 350
		dvb_dmx_swfilter_section_new(feed);
	}

	if (buf[1] & 0x40) {
351
		/* PUSI=1 (is set), section boundary is here */
Linus Torvalds's avatar
Linus Torvalds committed
352
		if (count > 1 && buf[p] < count) {
353
			const u8 *before = &buf[p + 1];
Linus Torvalds's avatar
Linus Torvalds committed
354
			u8 before_len = buf[p];
355 356
			const u8 *after = &before[before_len];
			u8 after_len = count - 1 - before_len;
Linus Torvalds's avatar
Linus Torvalds committed
357

358 359
			dvb_dmx_swfilter_section_copy_dump(feed, before,
							   before_len);
360 361
			/* before start of new section, set pusi_seen */
			feed->pusi_seen = true;
Linus Torvalds's avatar
Linus Torvalds committed
362
			dvb_dmx_swfilter_section_new(feed);
363 364
			dvb_dmx_swfilter_section_copy_dump(feed, after,
							   after_len);
365 366 367 368
		} else if (count > 0) {
			set_buf_flags(feed,
				      DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
			dprintk_sect_loss("PUSI=1 but %d bytes lost\n", count);
Linus Torvalds's avatar
Linus Torvalds committed
369 370
		}
	} else {
371 372
		/* 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
373
	}
374

Linus Torvalds's avatar
Linus Torvalds committed
375 376 377
	return 0;
}

378 379
static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed,
						const u8 *buf)
Linus Torvalds's avatar
Linus Torvalds committed
380
{
381
	switch (feed->type) {
Linus Torvalds's avatar
Linus Torvalds committed
382 383 384 385 386 387 388
	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
389 390
				feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts,
					    &feed->buffer_flags);
Linus Torvalds's avatar
Linus Torvalds committed
391
		}
392
		/* Used only on full-featured devices */
Linus Torvalds's avatar
Linus Torvalds committed
393 394 395 396 397 398 399 400 401
		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)
402
			feed->feed.sec.seclen = feed->feed.sec.secbufp = 0;
Linus Torvalds's avatar
Linus Torvalds committed
403 404 405 406 407 408 409 410 411 412
		break;

	default:
		break;
	}
}

#define DVR_FEED(f)							\
	(((f)->type == DMX_TYPE_TS) &&					\
	((f)->feed.ts.is_filtering) &&					\
413
	(((f)->ts_type & (TS_PACKET | TS_DEMUX)) == TS_PACKET))
Linus Torvalds's avatar
Linus Torvalds committed
414 415 416 417 418 419 420

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;

421
	if (dvb_demux_speedcheck) {
422
		ktime_t cur_time;
423 424 425 426 427 428
		u64 speed_bytes, speed_timedelta;

		demux->speed_pkts_cnt++;

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

431
			if (ktime_to_ns(demux->speed_last_time) != 0) {
432 433 434 435 436
				speed_bytes = (u64)demux->speed_pkts_cnt
					* 188 * 8;
				/* convert to 1024 basis */
				speed_bytes = 1000 * div64_u64(speed_bytes,
						1024);
437 438
				speed_timedelta = ktime_ms_delta(cur_time,
							demux->speed_last_time);
439 440 441 442
				if (speed_timedelta)
					dprintk("TS speed %llu Kbits/sec \n",
						div64_u64(speed_bytes,
							  speed_timedelta));
443
			}
444 445 446

			demux->speed_last_time = cur_time;
			demux->speed_pkts_cnt = 0;
447 448
		}
	}
449

450
	if (buf[1] & 0x80) {
451 452 453 454 455
		list_for_each_entry(feed, &demux->feed_list, list_head) {
			if ((feed->pid != pid) && (feed->pid != 0x2000))
				continue;
			set_buf_flags(feed, DMX_BUFFER_FLAG_TEI);
		}
456
		dprintk_tscheck("TEI detected. PID=0x%x data1=0x%x\n",
457
				pid, buf[1]);
458
		/* data in this packet can't be trusted - drop it unless
459 460 461 462
		 * 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 */
463 464 465 466 467 468 469 470
		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]) {
471 472 473 474 475 476 477
					list_for_each_entry(feed, &demux->feed_list, list_head) {
						if ((feed->pid != pid) && (feed->pid != 0x2000))
							continue;
						set_buf_flags(feed,
							      DMX_BUFFER_PKT_COUNTER_MISMATCH);
					}

478
					dprintk_tscheck("TS packet counter mismatch. PID=0x%x expected 0x%x got 0x%x\n",
479 480
							pid, demux->cnt_storage[pid],
							buf[3] & 0xf);
481 482 483 484
					demux->cnt_storage[pid] = buf[3] & 0xf;
				}
			}
			/* end check */
485
		}
486

487
	list_for_each_entry(feed, &demux->feed_list, list_head) {
Linus Torvalds's avatar
Linus Torvalds committed
488 489 490 491 492 493 494 495
		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;

496
		if (feed->pid == pid)
Linus Torvalds's avatar
Linus Torvalds committed
497
			dvb_dmx_swfilter_packet_type(feed, buf);
498
		else if (feed->pid == 0x2000)
499 500
			feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts,
				    &feed->buffer_flags);
Linus Torvalds's avatar
Linus Torvalds committed
501 502 503
	}
}

504 505
void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf,
			      size_t count)
Linus Torvalds's avatar
Linus Torvalds committed
506
{
507 508 509
	unsigned long flags;

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

	while (count--) {
512 513
		if (buf[0] == 0x47)
			dvb_dmx_swfilter_packet(demux, buf);
Linus Torvalds's avatar
Linus Torvalds committed
514 515 516
		buf += 188;
	}

517
	spin_unlock_irqrestore(&demux->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
518 519
}

520
EXPORT_SYMBOL(dvb_dmx_swfilter_packets);
Linus Torvalds's avatar
Linus Torvalds committed
521

522 523
static inline int find_next_packet(const u8 *buf, int pos, size_t count,
				   const int pktsize)
Linus Torvalds's avatar
Linus Torvalds committed
524
{
525
	int start = pos, lost;
Linus Torvalds's avatar
Linus Torvalds committed
526

527 528 529 530 531
	while (pos < count) {
		if (buf[pos] == 0x47 ||
		    (pktsize == 204 && buf[pos] == 0xB8))
			break;
		pos++;
Linus Torvalds's avatar
Linus Torvalds committed
532 533
	}

534 535 536 537 538 539 540
	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
541 542
	}

543
	return pos;
Linus Torvalds's avatar
Linus Torvalds committed
544
}
545

546 547 548
/* 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
549
{
550
	int p = 0, i, j;
551
	const u8 *q;
552
	unsigned long flags;
553

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

556
	if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */
557
		i = demux->tsbufp;
558
		j = pktsize - i;
559
		if (count < j) {
Linus Torvalds's avatar
Linus Torvalds committed
560 561 562 563 564
			memcpy(&demux->tsbuf[i], buf, count);
			demux->tsbufp += count;
			goto bailout;
		}
		memcpy(&demux->tsbuf[i], buf, j);
565 566
		if (demux->tsbuf[0] == 0x47) /* double check */
			dvb_dmx_swfilter_packet(demux, demux->tsbuf);
Linus Torvalds's avatar
Linus Torvalds committed
567 568 569 570
		demux->tsbufp = 0;
		p += j;
	}

571 572 573 574 575 576 577 578 579 580 581 582 583
	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
584
		}
585 586 587 588 589 590 591 592 593 594
		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
595 596 597
	}

bailout:
598
	spin_unlock_irqrestore(&demux->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
599 600
}

601 602 603 604 605 606 607 608 609 610
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);
}
611
EXPORT_SYMBOL(dvb_dmx_swfilter_204);
Linus Torvalds's avatar
Linus Torvalds committed
612

613 614
void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count)
{
615 616 617
	unsigned long flags;

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

619 620
	demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts,
			   &demux->feed->buffer_flags);
621

622
	spin_unlock_irqrestore(&demux->lock, flags);
623 624 625
}
EXPORT_SYMBOL(dvb_dmx_swfilter_raw);

626
static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux)
Linus Torvalds's avatar
Linus Torvalds committed
627 628 629
{
	int i;

630
	for (i = 0; i < demux->filternum; i++)
Linus Torvalds's avatar
Linus Torvalds committed
631 632 633 634 635 636 637 638 639 640 641
		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];
}

642
static struct dvb_demux_feed *dvb_dmx_feed_alloc(struct dvb_demux *demux)
Linus Torvalds's avatar
Linus Torvalds committed
643 644 645
{
	int i;

646
	for (i = 0; i < demux->feednum; i++)
Linus Torvalds's avatar
Linus Torvalds committed
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
		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)) {
673
		pr_err("%s: feed already in list (type=%x state=%x pid=%x)\n",
674
		       __func__, feed->type, feed->state, feed->pid);
Linus Torvalds's avatar
Linus Torvalds committed
675 676 677 678 679 680 681 682 683 684 685 686
		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))) {
687
		pr_err("%s: feed not in list (type=%x state=%x pid=%x)\n",
688
		       __func__, feed->type, feed->state, feed->pid);
Linus Torvalds's avatar
Linus Torvalds committed
689 690 691 692 693 694 695 696
		goto out;
	}

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

697
static int dmx_ts_feed_set(struct dmx_ts_feed *ts_feed, u16 pid, int ts_type,
698
			   enum dmx_ts_pes pes_type, ktime_t timeout)
Linus Torvalds's avatar
Linus Torvalds committed
699
{
700
	struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
Linus Torvalds's avatar
Linus Torvalds committed
701 702 703 704 705
	struct dvb_demux *demux = feed->demux;

	if (pid > DMX_MAX_PID)
		return -EINVAL;

706
	if (mutex_lock_interruptible(&demux->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
707 708 709
		return -ERESTARTSYS;

	if (ts_type & TS_DECODER) {
710
		if (pes_type >= DMX_PES_OTHER) {
711
			mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
712 713 714 715 716
			return -EINVAL;
		}

		if (demux->pesfilter[pes_type] &&
		    demux->pesfilter[pes_type] != feed) {
717
			mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
			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;
733
	mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
734 735 736 737

	return 0;
}

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

744
	if (mutex_lock_interruptible(&demux->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
745 746 747
		return -ERESTARTSYS;

	if (feed->state != DMX_STATE_READY || feed->type != DMX_TYPE_TS) {
748
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
749 750 751 752
		return -EINVAL;
	}

	if (!demux->start_feed) {
753
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
754 755 756 757
		return -ENODEV;
	}

	if ((ret = demux->start_feed(feed)) < 0) {
758
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
759 760 761 762 763 764 765
		return ret;
	}

	spin_lock_irq(&demux->lock);
	ts_feed->is_filtering = 1;
	feed->state = DMX_STATE_GO;
	spin_unlock_irq(&demux->lock);
766
	mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
767 768 769 770

	return 0;
}

771
static int dmx_ts_feed_stop_filtering(struct dmx_ts_feed *ts_feed)
Linus Torvalds's avatar
Linus Torvalds committed
772
{
773
	struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
Linus Torvalds's avatar
Linus Torvalds committed
774 775 776
	struct dvb_demux *demux = feed->demux;
	int ret;

777
	mutex_lock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
778 779

	if (feed->state < DMX_STATE_GO) {
780
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
781 782 783 784
		return -EINVAL;
	}

	if (!demux->stop_feed) {
785
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
786 787 788 789 790 791 792 793 794
		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);
795
	mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
796 797 798 799

	return ret;
}

800 801 802
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
803
{
804
	struct dvb_demux *demux = (struct dvb_demux *)dmx;
Linus Torvalds's avatar
Linus Torvalds committed
805 806
	struct dvb_demux_feed *feed;

807
	if (mutex_lock_interruptible(&demux->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
808 809 810
		return -ERESTARTSYS;

	if (!(feed = dvb_dmx_feed_alloc(demux))) {
811
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
812 813 814 815 816 817 818 819
		return -EBUSY;
	}

	feed->type = DMX_TYPE_TS;
	feed->cb.ts = callback;
	feed->demux = demux;
	feed->pid = 0xffff;
	feed->peslen = 0xfffa;
820
	feed->buffer_flags = 0;
Linus Torvalds's avatar
Linus Torvalds committed
821 822 823 824 825 826 827 828 829 830 831

	(*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;
832
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
833 834 835 836 837 838 839
		return -EBUSY;
	}

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

840
	mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
841 842 843 844

	return 0;
}

845 846
static int dvbdmx_release_ts_feed(struct dmx_demux *dmx,
				  struct dmx_ts_feed *ts_feed)
Linus Torvalds's avatar
Linus Torvalds committed
847
{
848 849
	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
850

851
	mutex_lock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
852 853

	if (feed->state == DMX_STATE_FREE) {
854
		mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
855 856 857 858 859 860 861 862 863 864
		return -EINVAL;
	}

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

	dvb_demux_feed_del(feed);

	feed->pid = 0xffff;

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

868
	mutex_unlock(&demux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
869 870 871 872 873 874 875
	return 0;
}

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

876 877
static int dmx_section_feed_allocate_filter(struct dmx_section_feed *feed,
					    struct dmx_section_filter **filter)
Linus Torvalds's avatar
Linus Torvalds committed
878
{
879
	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
Linus Torvalds's avatar
Linus Torvalds committed
880 881 882
	struct dvb_demux *dvbdemux = dvbdmxfeed->demux;
	struct dvb_demux_filter *dvbdmxfilter;

883
	if (mutex_lock_interruptible(&dvbdemux->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
884 885 886 887
		return -ERESTARTSYS;

	dvbdmxfilter = dvb_dmx_filter_alloc(dvbdemux);
	if (!dvbdmxfilter) {
888
		mutex_unlock(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
889 890 891 892 893 894 895 896 897 898 899 900 901 902
		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);

903
	mutex_unlock(&dvbdemux->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
904 905 906
	return 0;
}

907
static int dmx_section_feed_set(struct dmx_section_feed *feed,
908
				u16 pid, int check_crc)
Linus Torvalds's avatar
Linus Torvalds committed
909
{
910
	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
Linus Torvalds's avatar
Linus Torvalds committed
911 912 913 914 915
	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;

	if (pid > 0x1fff)
		return -EINVAL;

916
	if (mutex_lock_interruptible(&dvbdmx->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
917 918 919 920 921 922 923 924
		return -ERESTARTSYS;

	dvb_demux_feed_add(dvbdmxfeed);

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

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

936
	if (!(f = dvbdmxfeed->filter))
Linus Torvalds's avatar
Linus Torvalds committed
937 938 939
		return;
	do {
		sf = &f->filter;
940
		doneq = false;
941
		for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
942 943 944 945 946
			mode = sf->filter_mode[i];
			mask = sf->filter_mask[i];
			f->maskandmode[i] = mask & mode;
			doneq |= f->maskandnotmode[i] = mask & ~mode;
		}
947
		f->doneq = doneq ? true : false;
Linus Torvalds's avatar
Linus Torvalds committed
948 949 950 951 952
	} while ((f = f->next));
}

static int dmx_section_feed_start_filtering(struct dmx_section_feed *feed)
{
953
	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
Linus Torvalds's avatar
Linus Torvalds committed
954 955 956
	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
	int ret;

957
	if (mutex_lock_interruptible(&dvbdmx->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
958 959 960
		return -ERESTARTSYS;

	if (feed->is_filtering) {
961
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
962 963 964 965
		return -EBUSY;
	}

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

	prepare_secfilters(dvbdmxfeed);

	if ((ret = dvbdmx->start_feed(dvbdmxfeed)) < 0) {
983
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
984 985 986 987 988 989 990 991
		return ret;
	}

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

992
	mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
993 994 995
	return 0;
}

996
static int dmx_section_feed_stop_filtering(struct dmx_section_feed *feed)
Linus Torvalds's avatar
Linus Torvalds committed
997
{
998
	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
Linus Torvalds's avatar
Linus Torvalds committed
999 1000 1001
	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
	int ret;

1002
	mutex_lock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1003 1004

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

1016
	mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1017 1018 1019 1020
	return ret;
}

static int dmx_section_feed_release_filter(struct dmx_section_feed *feed,
1021
					   struct dmx_section_filter *filter)
Linus Torvalds's avatar
Linus Torvalds committed
1022
{
1023 1024
	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
1025 1026
	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;

1027
	mutex_lock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1028 1029

	if (dvbdmxfilter->feed != dvbdmxfeed) {
1030
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1031 1032 1033
		return -EINVAL;
	}

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

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

	if (f == dvbdmxfilter) {
		dvbdmxfeed->filter = dvbdmxfilter->next;
	} else {
1048
		while (f->next != dvbdmxfilter)
Linus Torvalds's avatar
Linus Torvalds committed
1049 1050 1051 1052 1053 1054
			f = f->next;
		f->next = f->next->next;
	}

	dvbdmxfilter->state = DMX_STATE_FREE;
	spin_unlock_irq(&dvbdmx->lock);
1055
	mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1056 1057 1058 1059 1060 1061 1062
	return 0;
}

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

1066
	if (mutex_lock_interruptible(&dvbdmx->mutex))
Linus Torvalds's avatar
Linus Torvalds committed
1067 1068 1069
		return -ERESTARTSYS;

	if (!(dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx))) {
1070
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1071 1072 1073 1074 1075 1076 1077
		return -EBUSY;
	}

	dvbdmxfeed->type = DMX_TYPE_SEC;
	dvbdmxfeed->cb.sec = callback;
	dvbdmxfeed->demux = dvbdmx;
	dvbdmxfeed->pid = 0xffff;
1078
	dvbdmxfeed->buffer_flags = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1079 1080 1081 1082 1083
	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;

1084
	(*feed) = &dvbdmxfeed->feed.sec;
Linus Torvalds's avatar
Linus Torvalds committed
1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
	(*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;

1095
	mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1096 1097 1098 1099 1100 1101
	return 0;
}

static int dvbdmx_release_section_feed(struct dmx_demux *demux,
				       struct dmx_section_feed *feed)
{
1102 1103
	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
1104

1105
	mutex_lock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1106

1107
	if (dvbdmxfeed->state == DMX_STATE_FREE) {
1108
		mutex_unlock(&dvbdmx->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1109 1110
		return -EINVAL;
	}
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 1251
	dvbdemux->filter = vmalloc(array_size(sizeof(struct dvb_demux_filter),
					      dvbdemux->filternum));
Linus Torvalds's avatar
Linus Torvalds committed
1252 1253 1254 1255

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

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