diff options
Diffstat (limited to 'drivers/media/dvb/dvb-core/dvb_demux.c')
-rw-r--r-- | drivers/media/dvb/dvb-core/dvb_demux.c | 1299 |
1 files changed, 1299 insertions, 0 deletions
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c new file mode 100644 index 00000000000..faa3671b649 --- /dev/null +++ b/drivers/media/dvb/dvb-core/dvb_demux.c | |||
@@ -0,0 +1,1299 @@ | |||
1 | /* | ||
2 | * dvb_demux.c - DVB kernel demux API | ||
3 | * | ||
4 | * Copyright (C) 2000-2001 Ralph Metzler <ralph@convergence.de> | ||
5 | * & Marcus Metzler <marcus@convergence.de> | ||
6 | * for convergence integrated media GmbH | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU Lesser General Public License | ||
10 | * as published by the Free Software Foundation; either version 2.1 | ||
11 | * of the License, or (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU Lesser General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <linux/sched.h> | ||
25 | #include <linux/spinlock.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/vmalloc.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/poll.h> | ||
30 | #include <linux/string.h> | ||
31 | #include <linux/crc32.h> | ||
32 | #include <asm/uaccess.h> | ||
33 | #include <asm/div64.h> | ||
34 | |||
35 | #include "dvb_demux.h" | ||
36 | |||
37 | #define NOBUFS | ||
38 | /* | ||
39 | ** #define DVB_DEMUX_SECTION_LOSS_LOG to monitor payload loss in the syslog | ||
40 | */ | ||
41 | // #define DVB_DEMUX_SECTION_LOSS_LOG | ||
42 | |||
43 | static int dvb_demux_tscheck; | ||
44 | module_param(dvb_demux_tscheck, int, 0644); | ||
45 | MODULE_PARM_DESC(dvb_demux_tscheck, | ||
46 | "enable transport stream continuity and TEI check"); | ||
47 | |||
48 | static int dvb_demux_speedcheck; | ||
49 | module_param(dvb_demux_speedcheck, int, 0644); | ||
50 | MODULE_PARM_DESC(dvb_demux_speedcheck, | ||
51 | "enable transport stream speed check"); | ||
52 | |||
53 | #define dprintk_tscheck(x...) do { \ | ||
54 | if (dvb_demux_tscheck && printk_ratelimit()) \ | ||
55 | printk(x); \ | ||
56 | } while (0) | ||
57 | |||
58 | /****************************************************************************** | ||
59 | * static inlined helper functions | ||
60 | ******************************************************************************/ | ||
61 | |||
62 | static inline u16 section_length(const u8 *buf) | ||
63 | { | ||
64 | return 3 + ((buf[1] & 0x0f) << 8) + buf[2]; | ||
65 | } | ||
66 | |||
67 | static inline u16 ts_pid(const u8 *buf) | ||
68 | { | ||
69 | return ((buf[1] & 0x1f) << 8) + buf[2]; | ||
70 | } | ||
71 | |||
72 | static inline u8 payload(const u8 *tsp) | ||
73 | { | ||
74 | if (!(tsp[3] & 0x10)) // no payload? | ||
75 | return 0; | ||
76 | |||
77 | if (tsp[3] & 0x20) { // adaptation field? | ||
78 | if (tsp[4] > 183) // corrupted data? | ||
79 | return 0; | ||
80 | else | ||
81 | return 184 - 1 - tsp[4]; | ||
82 | } | ||
83 | |||
84 | return 184; | ||
85 | } | ||
86 | |||
87 | static u32 dvb_dmx_crc32(struct dvb_demux_feed *f, const u8 *src, size_t len) | ||
88 | { | ||
89 | return (f->feed.sec.crc_val = crc32_be(f->feed.sec.crc_val, src, len)); | ||
90 | } | ||
91 | |||
92 | static void dvb_dmx_memcopy(struct dvb_demux_feed *f, u8 *d, const u8 *s, | ||
93 | size_t len) | ||
94 | { | ||
95 | memcpy(d, s, len); | ||
96 | } | ||
97 | |||
98 | /****************************************************************************** | ||
99 | * Software filter functions | ||
100 | ******************************************************************************/ | ||
101 | |||
102 | static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed, | ||
103 | const u8 *buf) | ||
104 | { | ||
105 | int count = payload(buf); | ||
106 | int p; | ||
107 | //int ccok; | ||
108 | //u8 cc; | ||
109 | |||
110 | if (count == 0) | ||
111 | return -1; | ||
112 | |||
113 | p = 188 - count; | ||
114 | |||
115 | /* | ||
116 | cc = buf[3] & 0x0f; | ||
117 | ccok = ((feed->cc + 1) & 0x0f) == cc; | ||
118 | feed->cc = cc; | ||
119 | if (!ccok) | ||
120 | printk("missed packet!\n"); | ||
121 | */ | ||
122 | |||
123 | if (buf[1] & 0x40) // PUSI ? | ||
124 | feed->peslen = 0xfffa; | ||
125 | |||
126 | feed->peslen += count; | ||
127 | |||
128 | return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts, DMX_OK); | ||
129 | } | ||
130 | |||
131 | static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed, | ||
132 | struct dvb_demux_filter *f) | ||
133 | { | ||
134 | u8 neq = 0; | ||
135 | int i; | ||
136 | |||
137 | for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) { | ||
138 | u8 xor = f->filter.filter_value[i] ^ feed->feed.sec.secbuf[i]; | ||
139 | |||
140 | if (f->maskandmode[i] & xor) | ||
141 | return 0; | ||
142 | |||
143 | neq |= f->maskandnotmode[i] & xor; | ||
144 | } | ||
145 | |||
146 | if (f->doneq && !neq) | ||
147 | return 0; | ||
148 | |||
149 | return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen, | ||
150 | NULL, 0, &f->filter, DMX_OK); | ||
151 | } | ||
152 | |||
153 | static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed) | ||
154 | { | ||
155 | struct dvb_demux *demux = feed->demux; | ||
156 | struct dvb_demux_filter *f = feed->filter; | ||
157 | struct dmx_section_feed *sec = &feed->feed.sec; | ||
158 | int section_syntax_indicator; | ||
159 | |||
160 | if (!sec->is_filtering) | ||
161 | return 0; | ||
162 | |||
163 | if (!f) | ||
164 | return 0; | ||
165 | |||
166 | if (sec->check_crc) { | ||
167 | section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0); | ||
168 | if (section_syntax_indicator && | ||
169 | demux->check_crc32(feed, sec->secbuf, sec->seclen)) | ||
170 | return -1; | ||
171 | } | ||
172 | |||
173 | do { | ||
174 | if (dvb_dmx_swfilter_sectionfilter(feed, f) < 0) | ||
175 | return -1; | ||
176 | } while ((f = f->next) && sec->is_filtering); | ||
177 | |||
178 | sec->seclen = 0; | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static void dvb_dmx_swfilter_section_new(struct dvb_demux_feed *feed) | ||
184 | { | ||
185 | struct dmx_section_feed *sec = &feed->feed.sec; | ||
186 | |||
187 | #ifdef DVB_DEMUX_SECTION_LOSS_LOG | ||
188 | if (sec->secbufp < sec->tsfeedp) { | ||
189 | int i, n = sec->tsfeedp - sec->secbufp; | ||
190 | |||
191 | /* | ||
192 | * Section padding is done with 0xff bytes entirely. | ||
193 | * Due to speed reasons, we won't check all of them | ||
194 | * but just first and last. | ||
195 | */ | ||
196 | if (sec->secbuf[0] != 0xff || sec->secbuf[n - 1] != 0xff) { | ||
197 | printk("dvb_demux.c section ts padding loss: %d/%d\n", | ||
198 | n, sec->tsfeedp); | ||
199 | printk("dvb_demux.c pad data:"); | ||
200 | for (i = 0; i < n; i++) | ||
201 | printk(" %02x", sec->secbuf[i]); | ||
202 | printk("\n"); | ||
203 | } | ||
204 | } | ||
205 | #endif | ||
206 | |||
207 | sec->tsfeedp = sec->secbufp = sec->seclen = 0; | ||
208 | sec->secbuf = sec->secbuf_base; | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | * Losless Section Demux 1.4.1 by Emard | ||
213 | * Valsecchi Patrick: | ||
214 | * - middle of section A (no PUSI) | ||
215 | * - end of section A and start of section B | ||
216 | * (with PUSI pointing to the start of the second section) | ||
217 | * | ||
218 | * In this case, without feed->pusi_seen you'll receive a garbage section | ||
219 | * consisting of the end of section A. Basically because tsfeedp | ||
220 | * is incemented and the use=0 condition is not raised | ||
221 | * when the second packet arrives. | ||
222 | * | ||
223 | * Fix: | ||
224 | * when demux is started, let feed->pusi_seen = 0 to | ||
225 | * prevent initial feeding of garbage from the end of | ||
226 | * previous section. When you for the first time see PUSI=1 | ||
227 | * then set feed->pusi_seen = 1 | ||
228 | */ | ||
229 | static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed, | ||
230 | const u8 *buf, u8 len) | ||
231 | { | ||
232 | struct dvb_demux *demux = feed->demux; | ||
233 | struct dmx_section_feed *sec = &feed->feed.sec; | ||
234 | u16 limit, seclen, n; | ||
235 | |||
236 | if (sec->tsfeedp >= DMX_MAX_SECFEED_SIZE) | ||
237 | return 0; | ||
238 | |||
239 | if (sec->tsfeedp + len > DMX_MAX_SECFEED_SIZE) { | ||
240 | #ifdef DVB_DEMUX_SECTION_LOSS_LOG | ||
241 | printk("dvb_demux.c section buffer full loss: %d/%d\n", | ||
242 | sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE, | ||
243 | DMX_MAX_SECFEED_SIZE); | ||
244 | #endif | ||
245 | len = DMX_MAX_SECFEED_SIZE - sec->tsfeedp; | ||
246 | } | ||
247 | |||
248 | if (len <= 0) | ||
249 | return 0; | ||
250 | |||
251 | demux->memcopy(feed, sec->secbuf_base + sec->tsfeedp, buf, len); | ||
252 | sec->tsfeedp += len; | ||
253 | |||
254 | /* | ||
255 | * Dump all the sections we can find in the data (Emard) | ||
256 | */ | ||
257 | limit = sec->tsfeedp; | ||
258 | if (limit > DMX_MAX_SECFEED_SIZE) | ||
259 | return -1; /* internal error should never happen */ | ||
260 | |||
261 | /* to be sure always set secbuf */ | ||
262 | sec->secbuf = sec->secbuf_base + sec->secbufp; | ||
263 | |||
264 | for (n = 0; sec->secbufp + 2 < limit; n++) { | ||
265 | seclen = section_length(sec->secbuf); | ||
266 | if (seclen <= 0 || seclen > DMX_MAX_SECTION_SIZE | ||
267 | || seclen + sec->secbufp > limit) | ||
268 | return 0; | ||
269 | sec->seclen = seclen; | ||
270 | sec->crc_val = ~0; | ||
271 | /* dump [secbuf .. secbuf+seclen) */ | ||
272 | if (feed->pusi_seen) | ||
273 | dvb_dmx_swfilter_section_feed(feed); | ||
274 | #ifdef DVB_DEMUX_SECTION_LOSS_LOG | ||
275 | else | ||
276 | printk("dvb_demux.c pusi not seen, discarding section data\n"); | ||
277 | #endif | ||
278 | sec->secbufp += seclen; /* secbufp and secbuf moving together is */ | ||
279 | sec->secbuf += seclen; /* redundant but saves pointer arithmetic */ | ||
280 | } | ||
281 | |||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed, | ||
286 | const u8 *buf) | ||
287 | { | ||
288 | u8 p, count; | ||
289 | int ccok, dc_i = 0; | ||
290 | u8 cc; | ||
291 | |||
292 | count = payload(buf); | ||
293 | |||
294 | if (count == 0) /* count == 0 if no payload or out of range */ | ||
295 | return -1; | ||
296 | |||
297 | p = 188 - count; /* payload start */ | ||
298 | |||
299 | cc = buf[3] & 0x0f; | ||
300 | ccok = ((feed->cc + 1) & 0x0f) == cc; | ||
301 | feed->cc = cc; | ||
302 | |||
303 | if (buf[3] & 0x20) { | ||
304 | /* adaption field present, check for discontinuity_indicator */ | ||
305 | if ((buf[4] > 0) && (buf[5] & 0x80)) | ||
306 | dc_i = 1; | ||
307 | } | ||
308 | |||
309 | if (!ccok || dc_i) { | ||
310 | #ifdef DVB_DEMUX_SECTION_LOSS_LOG | ||
311 | printk("dvb_demux.c discontinuity detected %d bytes lost\n", | ||
312 | count); | ||
313 | /* | ||
314 | * those bytes under sume circumstances will again be reported | ||
315 | * in the following dvb_dmx_swfilter_section_new | ||
316 | */ | ||
317 | #endif | ||
318 | /* | ||
319 | * Discontinuity detected. Reset pusi_seen = 0 to | ||
320 | * stop feeding of suspicious data until next PUSI=1 arrives | ||
321 | */ | ||
322 | feed->pusi_seen = 0; | ||
323 | dvb_dmx_swfilter_section_new(feed); | ||
324 | } | ||
325 | |||
326 | if (buf[1] & 0x40) { | ||
327 | /* PUSI=1 (is set), section boundary is here */ | ||
328 | if (count > 1 && buf[p] < count) { | ||
329 | const u8 *before = &buf[p + 1]; | ||
330 | u8 before_len = buf[p]; | ||
331 | const u8 *after = &before[before_len]; | ||
332 | u8 after_len = count - 1 - before_len; | ||
333 | |||
334 | dvb_dmx_swfilter_section_copy_dump(feed, before, | ||
335 | before_len); | ||
336 | /* before start of new section, set pusi_seen = 1 */ | ||
337 | feed->pusi_seen = 1; | ||
338 | dvb_dmx_swfilter_section_new(feed); | ||
339 | dvb_dmx_swfilter_section_copy_dump(feed, after, | ||
340 | after_len); | ||
341 | } | ||
342 | #ifdef DVB_DEMUX_SECTION_LOSS_LOG | ||
343 | else if (count > 0) | ||
344 | printk("dvb_demux.c PUSI=1 but %d bytes lost\n", count); | ||
345 | #endif | ||
346 | } else { | ||
347 | /* PUSI=0 (is not set), no section boundary */ | ||
348 | dvb_dmx_swfilter_section_copy_dump(feed, &buf[p], count); | ||
349 | } | ||
350 | |||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed, | ||
355 | const u8 *buf) | ||
356 | { | ||
357 | switch (feed->type) { | ||
358 | case DMX_TYPE_TS: | ||
359 | if (!feed->feed.ts.is_filtering) | ||
360 | break; | ||
361 | if (feed->ts_type & TS_PACKET) { | ||
362 | if (feed->ts_type & TS_PAYLOAD_ONLY) | ||
363 | dvb_dmx_swfilter_payload(feed, buf); | ||
364 | else | ||
365 | feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, | ||
366 | DMX_OK); | ||
367 | } | ||
368 | if (feed->ts_type & TS_DECODER) | ||
369 | if (feed->demux->write_to_decoder) | ||
370 | feed->demux->write_to_decoder(feed, buf, 188); | ||
371 | break; | ||
372 | |||
373 | case DMX_TYPE_SEC: | ||
374 | if (!feed->feed.sec.is_filtering) | ||
375 | break; | ||
376 | if (dvb_dmx_swfilter_section_packet(feed, buf) < 0) | ||
377 | feed->feed.sec.seclen = feed->feed.sec.secbufp = 0; | ||
378 | break; | ||
379 | |||
380 | default: | ||
381 | break; | ||
382 | } | ||
383 | } | ||
384 | |||
385 | #define DVR_FEED(f) \ | ||
386 | (((f)->type == DMX_TYPE_TS) && \ | ||
387 | ((f)->feed.ts.is_filtering) && \ | ||
388 | (((f)->ts_type & (TS_PACKET | TS_DEMUX)) == TS_PACKET)) | ||
389 | |||
390 | static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) | ||
391 | { | ||
392 | struct dvb_demux_feed *feed; | ||
393 | u16 pid = ts_pid(buf); | ||
394 | int dvr_done = 0; | ||
395 | |||
396 | if (dvb_demux_speedcheck) { | ||
397 | struct timespec cur_time, delta_time; | ||
398 | u64 speed_bytes, speed_timedelta; | ||
399 | |||
400 | demux->speed_pkts_cnt++; | ||
401 | |||
402 | /* show speed every SPEED_PKTS_INTERVAL packets */ | ||
403 | if (!(demux->speed_pkts_cnt % SPEED_PKTS_INTERVAL)) { | ||
404 | cur_time = current_kernel_time(); | ||
405 | |||
406 | if (demux->speed_last_time.tv_sec != 0 && | ||
407 | demux->speed_last_time.tv_nsec != 0) { | ||
408 | delta_time = timespec_sub(cur_time, | ||
409 | demux->speed_last_time); | ||
410 | speed_bytes = (u64)demux->speed_pkts_cnt | ||
411 | * 188 * 8; | ||
412 | /* convert to 1024 basis */ | ||
413 | speed_bytes = 1000 * div64_u64(speed_bytes, | ||
414 | 1024); | ||
415 | speed_timedelta = | ||
416 | (u64)timespec_to_ns(&delta_time); | ||
417 | speed_timedelta = div64_u64(speed_timedelta, | ||
418 | 1000000); /* nsec -> usec */ | ||
419 | printk(KERN_INFO "TS speed %llu Kbits/sec \n", | ||
420 | div64_u64(speed_bytes, | ||
421 | speed_timedelta)); | ||
422 | }; | ||
423 | |||
424 | demux->speed_last_time = cur_time; | ||
425 | demux->speed_pkts_cnt = 0; | ||
426 | }; | ||
427 | }; | ||
428 | |||
429 | if (demux->cnt_storage && dvb_demux_tscheck) { | ||
430 | /* check pkt counter */ | ||
431 | if (pid < MAX_PID) { | ||
432 | if (buf[1] & 0x80) | ||
433 | dprintk_tscheck("TEI detected. " | ||
434 | "PID=0x%x data1=0x%x\n", | ||
435 | pid, buf[1]); | ||
436 | |||
437 | if ((buf[3] & 0xf) != demux->cnt_storage[pid]) | ||
438 | dprintk_tscheck("TS packet counter mismatch. " | ||
439 | "PID=0x%x expected 0x%x " | ||
440 | "got 0x%x\n", | ||
441 | pid, demux->cnt_storage[pid], | ||
442 | buf[3] & 0xf); | ||
443 | |||
444 | demux->cnt_storage[pid] = ((buf[3] & 0xf) + 1)&0xf; | ||
445 | }; | ||
446 | /* end check */ | ||
447 | }; | ||
448 | |||
449 | list_for_each_entry(feed, &demux->feed_list, list_head) { | ||
450 | if ((feed->pid != pid) && (feed->pid != 0x2000)) | ||
451 | continue; | ||
452 | |||
453 | /* copy each packet only once to the dvr device, even | ||
454 | * if a PID is in multiple filters (e.g. video + PCR) */ | ||
455 | if ((DVR_FEED(feed)) && (dvr_done++)) | ||
456 | continue; | ||
457 | |||
458 | if (feed->pid == pid) | ||
459 | dvb_dmx_swfilter_packet_type(feed, buf); | ||
460 | else if (feed->pid == 0x2000) | ||
461 | feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK); | ||
462 | } | ||
463 | } | ||
464 | |||
465 | void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf, | ||
466 | size_t count) | ||
467 | { | ||
468 | spin_lock(&demux->lock); | ||
469 | |||
470 | while (count--) { | ||
471 | if (buf[0] == 0x47) | ||
472 | dvb_dmx_swfilter_packet(demux, buf); | ||
473 | buf += 188; | ||
474 | } | ||
475 | |||
476 | spin_unlock(&demux->lock); | ||
477 | } | ||
478 | |||
479 | EXPORT_SYMBOL(dvb_dmx_swfilter_packets); | ||
480 | |||
481 | static inline int find_next_packet(const u8 *buf, int pos, size_t count, | ||
482 | const int pktsize) | ||
483 | { | ||
484 | int start = pos, lost; | ||
485 | |||
486 | while (pos < count) { | ||
487 | if (buf[pos] == 0x47 || | ||
488 | (pktsize == 204 && buf[pos] == 0xB8)) | ||
489 | break; | ||
490 | pos++; | ||
491 | } | ||
492 | |||
493 | lost = pos - start; | ||
494 | if (lost) { | ||
495 | /* This garbage is part of a valid packet? */ | ||
496 | int backtrack = pos - pktsize; | ||
497 | if (backtrack >= 0 && (buf[backtrack] == 0x47 || | ||
498 | (pktsize == 204 && buf[backtrack] == 0xB8))) | ||
499 | return backtrack; | ||
500 | } | ||
501 | |||
502 | return pos; | ||
503 | } | ||
504 | |||
505 | /* Filter all pktsize= 188 or 204 sized packets and skip garbage. */ | ||
506 | static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, | ||
507 | size_t count, const int pktsize) | ||
508 | { | ||
509 | int p = 0, i, j; | ||
510 | const u8 *q; | ||
511 | |||
512 | spin_lock(&demux->lock); | ||
513 | |||
514 | if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */ | ||
515 | i = demux->tsbufp; | ||
516 | j = pktsize - i; | ||
517 | if (count < j) { | ||
518 | memcpy(&demux->tsbuf[i], buf, count); | ||
519 | demux->tsbufp += count; | ||
520 | goto bailout; | ||
521 | } | ||
522 | memcpy(&demux->tsbuf[i], buf, j); | ||
523 | if (demux->tsbuf[0] == 0x47) /* double check */ | ||
524 | dvb_dmx_swfilter_packet(demux, demux->tsbuf); | ||
525 | demux->tsbufp = 0; | ||
526 | p += j; | ||
527 | } | ||
528 | |||
529 | while (1) { | ||
530 | p = find_next_packet(buf, p, count, pktsize); | ||
531 | if (p >= count) | ||
532 | break; | ||
533 | if (count - p < pktsize) | ||
534 | break; | ||
535 | |||
536 | q = &buf[p]; | ||
537 | |||
538 | if (pktsize == 204 && (*q == 0xB8)) { | ||
539 | memcpy(demux->tsbuf, q, 188); | ||
540 | demux->tsbuf[0] = 0x47; | ||
541 | q = demux->tsbuf; | ||
542 | } | ||
543 | dvb_dmx_swfilter_packet(demux, q); | ||
544 | p += pktsize; | ||
545 | } | ||
546 | |||
547 | i = count - p; | ||
548 | if (i) { | ||
549 | memcpy(demux->tsbuf, &buf[p], i); | ||
550 | demux->tsbufp = i; | ||
551 | if (pktsize == 204 && demux->tsbuf[0] == 0xB8) | ||
552 | demux->tsbuf[0] = 0x47; | ||
553 | } | ||
554 | |||
555 | bailout: | ||
556 | spin_unlock(&demux->lock); | ||
557 | } | ||
558 | |||
559 | void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count) | ||
560 | { | ||
561 | _dvb_dmx_swfilter(demux, buf, count, 188); | ||
562 | } | ||
563 | EXPORT_SYMBOL(dvb_dmx_swfilter); | ||
564 | |||
565 | void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count) | ||
566 | { | ||
567 | _dvb_dmx_swfilter(demux, buf, count, 204); | ||
568 | } | ||
569 | EXPORT_SYMBOL(dvb_dmx_swfilter_204); | ||
570 | |||
571 | static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux) | ||
572 | { | ||
573 | int i; | ||
574 | |||
575 | for (i = 0; i < demux->filternum; i++) | ||
576 | if (demux->filter[i].state == DMX_STATE_FREE) | ||
577 | break; | ||
578 | |||
579 | if (i == demux->filternum) | ||
580 | return NULL; | ||
581 | |||
582 | demux->filter[i].state = DMX_STATE_ALLOCATED; | ||
583 | |||
584 | return &demux->filter[i]; | ||
585 | } | ||
586 | |||
587 | static struct dvb_demux_feed *dvb_dmx_feed_alloc(struct dvb_demux *demux) | ||
588 | { | ||
589 | int i; | ||
590 | |||
591 | for (i = 0; i < demux->feednum; i++) | ||
592 | if (demux->feed[i].state == DMX_STATE_FREE) | ||
593 | break; | ||
594 | |||
595 | if (i == demux->feednum) | ||
596 | return NULL; | ||
597 | |||
598 | demux->feed[i].state = DMX_STATE_ALLOCATED; | ||
599 | |||
600 | return &demux->feed[i]; | ||
601 | } | ||
602 | |||
603 | static int dvb_demux_feed_find(struct dvb_demux_feed *feed) | ||
604 | { | ||
605 | struct dvb_demux_feed *entry; | ||
606 | |||
607 | list_for_each_entry(entry, &feed->demux->feed_list, list_head) | ||
608 | if (entry == feed) | ||
609 | return 1; | ||
610 | |||
611 | return 0; | ||
612 | } | ||
613 | |||
614 | static void dvb_demux_feed_add(struct dvb_demux_feed *feed) | ||
615 | { | ||
616 | spin_lock_irq(&feed->demux->lock); | ||
617 | if (dvb_demux_feed_find(feed)) { | ||
618 | printk(KERN_ERR "%s: feed already in list (type=%x state=%x pid=%x)\n", | ||
619 | __func__, feed->type, feed->state, feed->pid); | ||
620 | goto out; | ||
621 | } | ||
622 | |||
623 | list_add(&feed->list_head, &feed->demux->feed_list); | ||
624 | out: | ||
625 | spin_unlock_irq(&feed->demux->lock); | ||
626 | } | ||
627 | |||
628 | static void dvb_demux_feed_del(struct dvb_demux_feed *feed) | ||
629 | { | ||
630 | spin_lock_irq(&feed->demux->lock); | ||
631 | if (!(dvb_demux_feed_find(feed))) { | ||
632 | printk(KERN_ERR "%s: feed not in list (type=%x state=%x pid=%x)\n", | ||
633 | __func__, feed->type, feed->state, feed->pid); | ||
634 | goto out; | ||
635 | } | ||
636 | |||
637 | list_del(&feed->list_head); | ||
638 | out: | ||
639 | spin_unlock_irq(&feed->demux->lock); | ||
640 | } | ||
641 | |||
642 | static int dmx_ts_feed_set(struct dmx_ts_feed *ts_feed, u16 pid, int ts_type, | ||
643 | enum dmx_ts_pes pes_type, | ||
644 | size_t circular_buffer_size, struct timespec timeout) | ||
645 | { | ||
646 | struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; | ||
647 | struct dvb_demux *demux = feed->demux; | ||
648 | |||
649 | if (pid > DMX_MAX_PID) | ||
650 | return -EINVAL; | ||
651 | |||
652 | if (mutex_lock_interruptible(&demux->mutex)) | ||
653 | return -ERESTARTSYS; | ||
654 | |||
655 | if (ts_type & TS_DECODER) { | ||
656 | if (pes_type >= DMX_TS_PES_OTHER) { | ||
657 | mutex_unlock(&demux->mutex); | ||
658 | return -EINVAL; | ||
659 | } | ||
660 | |||
661 | if (demux->pesfilter[pes_type] && | ||
662 | demux->pesfilter[pes_type] != feed) { | ||
663 | mutex_unlock(&demux->mutex); | ||
664 | return -EINVAL; | ||
665 | } | ||
666 | |||
667 | demux->pesfilter[pes_type] = feed; | ||
668 | demux->pids[pes_type] = pid; | ||
669 | } | ||
670 | |||
671 | dvb_demux_feed_add(feed); | ||
672 | |||
673 | feed->pid = pid; | ||
674 | feed->buffer_size = circular_buffer_size; | ||
675 | feed->timeout = timeout; | ||
676 | feed->ts_type = ts_type; | ||
677 | feed->pes_type = pes_type; | ||
678 | |||
679 | if (feed->buffer_size) { | ||
680 | #ifdef NOBUFS | ||
681 | feed->buffer = NULL; | ||
682 | #else | ||
683 | feed->buffer = vmalloc(feed->buffer_size); | ||
684 | if (!feed->buffer) { | ||
685 | mutex_unlock(&demux->mutex); | ||
686 | return -ENOMEM; | ||
687 | } | ||
688 | #endif | ||
689 | } | ||
690 | |||
691 | feed->state = DMX_STATE_READY; | ||
692 | mutex_unlock(&demux->mutex); | ||
693 | |||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | static int dmx_ts_feed_start_filtering(struct dmx_ts_feed *ts_feed) | ||
698 | { | ||
699 | struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; | ||
700 | struct dvb_demux *demux = feed->demux; | ||
701 | int ret; | ||
702 | |||
703 | if (mutex_lock_interruptible(&demux->mutex)) | ||
704 | return -ERESTARTSYS; | ||
705 | |||
706 | if (feed->state != DMX_STATE_READY || feed->type != DMX_TYPE_TS) { | ||
707 | mutex_unlock(&demux->mutex); | ||
708 | return -EINVAL; | ||
709 | } | ||
710 | |||
711 | if (!demux->start_feed) { | ||
712 | mutex_unlock(&demux->mutex); | ||
713 | return -ENODEV; | ||
714 | } | ||
715 | |||
716 | if ((ret = demux->start_feed(feed)) < 0) { | ||
717 | mutex_unlock(&demux->mutex); | ||
718 | return ret; | ||
719 | } | ||
720 | |||
721 | spin_lock_irq(&demux->lock); | ||
722 | ts_feed->is_filtering = 1; | ||
723 | feed->state = DMX_STATE_GO; | ||
724 | spin_unlock_irq(&demux->lock); | ||
725 | mutex_unlock(&demux->mutex); | ||
726 | |||
727 | return 0; | ||
728 | } | ||
729 | |||
730 | static int dmx_ts_feed_stop_filtering(struct dmx_ts_feed *ts_feed) | ||
731 | { | ||
732 | struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; | ||
733 | struct dvb_demux *demux = feed->demux; | ||
734 | int ret; | ||
735 | |||
736 | mutex_lock(&demux->mutex); | ||
737 | |||
738 | if (feed->state < DMX_STATE_GO) { | ||
739 | mutex_unlock(&demux->mutex); | ||
740 | return -EINVAL; | ||
741 | } | ||
742 | |||
743 | if (!demux->stop_feed) { | ||
744 | mutex_unlock(&demux->mutex); | ||
745 | return -ENODEV; | ||
746 | } | ||
747 | |||
748 | ret = demux->stop_feed(feed); | ||
749 | |||
750 | spin_lock_irq(&demux->lock); | ||
751 | ts_feed->is_filtering = 0; | ||
752 | feed->state = DMX_STATE_ALLOCATED; | ||
753 | spin_unlock_irq(&demux->lock); | ||
754 | mutex_unlock(&demux->mutex); | ||
755 | |||
756 | return ret; | ||
757 | } | ||
758 | |||
759 | static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx, | ||
760 | struct dmx_ts_feed **ts_feed, | ||
761 | dmx_ts_cb callback) | ||
762 | { | ||
763 | struct dvb_demux *demux = (struct dvb_demux *)dmx; | ||
764 | struct dvb_demux_feed *feed; | ||
765 | |||
766 | if (mutex_lock_interruptible(&demux->mutex)) | ||
767 | return -ERESTARTSYS; | ||
768 | |||
769 | if (!(feed = dvb_dmx_feed_alloc(demux))) { | ||
770 | mutex_unlock(&demux->mutex); | ||
771 | return -EBUSY; | ||
772 | } | ||
773 | |||
774 | feed->type = DMX_TYPE_TS; | ||
775 | feed->cb.ts = callback; | ||
776 | feed->demux = demux; | ||
777 | feed->pid = 0xffff; | ||
778 | feed->peslen = 0xfffa; | ||
779 | feed->buffer = NULL; | ||
780 | |||
781 | (*ts_feed) = &feed->feed.ts; | ||
782 | (*ts_feed)->parent = dmx; | ||
783 | (*ts_feed)->priv = NULL; | ||
784 | (*ts_feed)->is_filtering = 0; | ||
785 | (*ts_feed)->start_filtering = dmx_ts_feed_start_filtering; | ||
786 | (*ts_feed)->stop_filtering = dmx_ts_feed_stop_filtering; | ||
787 | (*ts_feed)->set = dmx_ts_feed_set; | ||
788 | |||
789 | if (!(feed->filter = dvb_dmx_filter_alloc(demux))) { | ||
790 | feed->state = DMX_STATE_FREE; | ||
791 | mutex_unlock(&demux->mutex); | ||
792 | return -EBUSY; | ||
793 | } | ||
794 | |||
795 | feed->filter->type = DMX_TYPE_TS; | ||
796 | feed->filter->feed = feed; | ||
797 | feed->filter->state = DMX_STATE_READY; | ||
798 | |||
799 | mutex_unlock(&demux->mutex); | ||
800 | |||
801 | return 0; | ||
802 | } | ||
803 | |||
804 | static int dvbdmx_release_ts_feed(struct dmx_demux *dmx, | ||
805 | struct dmx_ts_feed *ts_feed) | ||
806 | { | ||
807 | struct dvb_demux *demux = (struct dvb_demux *)dmx; | ||
808 | struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; | ||
809 | |||
810 | mutex_lock(&demux->mutex); | ||
811 | |||
812 | if (feed->state == DMX_STATE_FREE) { | ||
813 | mutex_unlock(&demux->mutex); | ||
814 | return -EINVAL; | ||
815 | } | ||
816 | #ifndef NOBUFS | ||
817 | vfree(feed->buffer); | ||
818 | feed->buffer = NULL; | ||
819 | #endif | ||
820 | |||
821 | feed->state = DMX_STATE_FREE; | ||
822 | feed->filter->state = DMX_STATE_FREE; | ||
823 | |||
824 | dvb_demux_feed_del(feed); | ||
825 | |||
826 | feed->pid = 0xffff; | ||
827 | |||
828 | if (feed->ts_type & TS_DECODER && feed->pes_type < DMX_TS_PES_OTHER) | ||
829 | demux->pesfilter[feed->pes_type] = NULL; | ||
830 | |||
831 | mutex_unlock(&demux->mutex); | ||
832 | return 0; | ||
833 | } | ||
834 | |||
835 | /****************************************************************************** | ||
836 | * dmx_section_feed API calls | ||
837 | ******************************************************************************/ | ||
838 | |||
839 | static int dmx_section_feed_allocate_filter(struct dmx_section_feed *feed, | ||
840 | struct dmx_section_filter **filter) | ||
841 | { | ||
842 | struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; | ||
843 | struct dvb_demux *dvbdemux = dvbdmxfeed->demux; | ||
844 | struct dvb_demux_filter *dvbdmxfilter; | ||
845 | |||
846 | if (mutex_lock_interruptible(&dvbdemux->mutex)) | ||
847 | return -ERESTARTSYS; | ||
848 | |||
849 | dvbdmxfilter = dvb_dmx_filter_alloc(dvbdemux); | ||
850 | if (!dvbdmxfilter) { | ||
851 | mutex_unlock(&dvbdemux->mutex); | ||
852 | return -EBUSY; | ||
853 | } | ||
854 | |||
855 | spin_lock_irq(&dvbdemux->lock); | ||
856 | *filter = &dvbdmxfilter->filter; | ||
857 | (*filter)->parent = feed; | ||
858 | (*filter)->priv = NULL; | ||
859 | dvbdmxfilter->feed = dvbdmxfeed; | ||
860 | dvbdmxfilter->type = DMX_TYPE_SEC; | ||
861 | dvbdmxfilter->state = DMX_STATE_READY; | ||
862 | dvbdmxfilter->next = dvbdmxfeed->filter; | ||
863 | dvbdmxfeed->filter = dvbdmxfilter; | ||
864 | spin_unlock_irq(&dvbdemux->lock); | ||
865 | |||
866 | mutex_unlock(&dvbdemux->mutex); | ||
867 | return 0; | ||
868 | } | ||
869 | |||
870 | static int dmx_section_feed_set(struct dmx_section_feed *feed, | ||
871 | u16 pid, size_t circular_buffer_size, | ||
872 | int check_crc) | ||
873 | { | ||
874 | struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; | ||
875 | struct dvb_demux *dvbdmx = dvbdmxfeed->demux; | ||
876 | |||
877 | if (pid > 0x1fff) | ||
878 | return -EINVAL; | ||
879 | |||
880 | if (mutex_lock_interruptible(&dvbdmx->mutex)) | ||
881 | return -ERESTARTSYS; | ||
882 | |||
883 | dvb_demux_feed_add(dvbdmxfeed); | ||
884 | |||
885 | dvbdmxfeed->pid = pid; | ||
886 | dvbdmxfeed->buffer_size = circular_buffer_size; | ||
887 | dvbdmxfeed->feed.sec.check_crc = check_crc; | ||
888 | |||
889 | #ifdef NOBUFS | ||
890 | dvbdmxfeed->buffer = NULL; | ||
891 | #else | ||
892 | dvbdmxfeed->buffer = vmalloc(dvbdmxfeed->buffer_size); | ||
893 | if (!dvbdmxfeed->buffer) { | ||
894 | mutex_unlock(&dvbdmx->mutex); | ||
895 | return -ENOMEM; | ||
896 | } | ||
897 | #endif | ||
898 | |||
899 | dvbdmxfeed->state = DMX_STATE_READY; | ||
900 | mutex_unlock(&dvbdmx->mutex); | ||
901 | return 0; | ||
902 | } | ||
903 | |||
904 | static void prepare_secfilters(struct dvb_demux_feed *dvbdmxfeed) | ||
905 | { | ||
906 | int i; | ||
907 | struct dvb_demux_filter *f; | ||
908 | struct dmx_section_filter *sf; | ||
909 | u8 mask, mode, doneq; | ||
910 | |||
911 | if (!(f = dvbdmxfeed->filter)) | ||
912 | return; | ||
913 | do { | ||
914 | sf = &f->filter; | ||
915 | doneq = 0; | ||
916 | for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) { | ||
917 | mode = sf->filter_mode[i]; | ||
918 | mask = sf->filter_mask[i]; | ||
919 | f->maskandmode[i] = mask & mode; | ||
920 | doneq |= f->maskandnotmode[i] = mask & ~mode; | ||
921 | } | ||
922 | f->doneq = doneq ? 1 : 0; | ||
923 | } while ((f = f->next)); | ||
924 | } | ||
925 | |||
926 | static int dmx_section_feed_start_filtering(struct dmx_section_feed *feed) | ||
927 | { | ||
928 | struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; | ||
929 | struct dvb_demux *dvbdmx = dvbdmxfeed->demux; | ||
930 | int ret; | ||
931 | |||
932 | if (mutex_lock_interruptible(&dvbdmx->mutex)) | ||
933 | return -ERESTARTSYS; | ||
934 | |||
935 | if (feed->is_filtering) { | ||
936 | mutex_unlock(&dvbdmx->mutex); | ||
937 | return -EBUSY; | ||
938 | } | ||
939 | |||
940 | if (!dvbdmxfeed->filter) { | ||
941 | mutex_unlock(&dvbdmx->mutex); | ||
942 | return -EINVAL; | ||
943 | } | ||
944 | |||
945 | dvbdmxfeed->feed.sec.tsfeedp = 0; | ||
946 | dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base; | ||
947 | dvbdmxfeed->feed.sec.secbufp = 0; | ||
948 | dvbdmxfeed->feed.sec.seclen = 0; | ||
949 | |||
950 | if (!dvbdmx->start_feed) { | ||
951 | mutex_unlock(&dvbdmx->mutex); | ||
952 | return -ENODEV; | ||
953 | } | ||
954 | |||
955 | prepare_secfilters(dvbdmxfeed); | ||
956 | |||
957 | if ((ret = dvbdmx->start_feed(dvbdmxfeed)) < 0) { | ||
958 | mutex_unlock(&dvbdmx->mutex); | ||
959 | return ret; | ||
960 | } | ||
961 | |||
962 | spin_lock_irq(&dvbdmx->lock); | ||
963 | feed->is_filtering = 1; | ||
964 | dvbdmxfeed->state = DMX_STATE_GO; | ||
965 | spin_unlock_irq(&dvbdmx->lock); | ||
966 | |||
967 | mutex_unlock(&dvbdmx->mutex); | ||
968 | return 0; | ||
969 | } | ||
970 | |||
971 | static int dmx_section_feed_stop_filtering(struct dmx_section_feed *feed) | ||
972 | { | ||
973 | struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; | ||
974 | struct dvb_demux *dvbdmx = dvbdmxfeed->demux; | ||
975 | int ret; | ||
976 | |||
977 | mutex_lock(&dvbdmx->mutex); | ||
978 | |||
979 | if (!dvbdmx->stop_feed) { | ||
980 | mutex_unlock(&dvbdmx->mutex); | ||
981 | return -ENODEV; | ||
982 | } | ||
983 | |||
984 | ret = dvbdmx->stop_feed(dvbdmxfeed); | ||
985 | |||
986 | spin_lock_irq(&dvbdmx->lock); | ||
987 | dvbdmxfeed->state = DMX_STATE_READY; | ||
988 | feed->is_filtering = 0; | ||
989 | spin_unlock_irq(&dvbdmx->lock); | ||
990 | |||
991 | mutex_unlock(&dvbdmx->mutex); | ||
992 | return ret; | ||
993 | } | ||
994 | |||
995 | static int dmx_section_feed_release_filter(struct dmx_section_feed *feed, | ||
996 | struct dmx_section_filter *filter) | ||
997 | { | ||
998 | struct dvb_demux_filter *dvbdmxfilter = (struct dvb_demux_filter *)filter, *f; | ||
999 | struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; | ||
1000 | struct dvb_demux *dvbdmx = dvbdmxfeed->demux; | ||
1001 | |||
1002 | mutex_lock(&dvbdmx->mutex); | ||
1003 | |||
1004 | if (dvbdmxfilter->feed != dvbdmxfeed) { | ||
1005 | mutex_unlock(&dvbdmx->mutex); | ||
1006 | return -EINVAL; | ||
1007 | } | ||
1008 | |||
1009 | if (feed->is_filtering) | ||
1010 | feed->stop_filtering(feed); | ||
1011 | |||
1012 | spin_lock_irq(&dvbdmx->lock); | ||
1013 | f = dvbdmxfeed->filter; | ||
1014 | |||
1015 | if (f == dvbdmxfilter) { | ||
1016 | dvbdmxfeed->filter = dvbdmxfilter->next; | ||
1017 | } else { | ||
1018 | while (f->next != dvbdmxfilter) | ||
1019 | f = f->next; | ||
1020 | f->next = f->next->next; | ||
1021 | } | ||
1022 | |||
1023 | dvbdmxfilter->state = DMX_STATE_FREE; | ||
1024 | spin_unlock_irq(&dvbdmx->lock); | ||
1025 | mutex_unlock(&dvbdmx->mutex); | ||
1026 | return 0; | ||
1027 | } | ||
1028 | |||
1029 | static int dvbdmx_allocate_section_feed(struct dmx_demux *demux, | ||
1030 | struct dmx_section_feed **feed, | ||
1031 | dmx_section_cb callback) | ||
1032 | { | ||
1033 | struct dvb_demux *dvbdmx = (struct dvb_demux *)demux; | ||
1034 | struct dvb_demux_feed *dvbdmxfeed; | ||
1035 | |||
1036 | if (mutex_lock_interruptible(&dvbdmx->mutex)) | ||
1037 | return -ERESTARTSYS; | ||
1038 | |||
1039 | if (!(dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx))) { | ||
1040 | mutex_unlock(&dvbdmx->mutex); | ||
1041 | return -EBUSY; | ||
1042 | } | ||
1043 | |||
1044 | dvbdmxfeed->type = DMX_TYPE_SEC; | ||
1045 | dvbdmxfeed->cb.sec = callback; | ||
1046 | dvbdmxfeed->demux = dvbdmx; | ||
1047 | dvbdmxfeed->pid = 0xffff; | ||
1048 | dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base; | ||
1049 | dvbdmxfeed->feed.sec.secbufp = dvbdmxfeed->feed.sec.seclen = 0; | ||
1050 | dvbdmxfeed->feed.sec.tsfeedp = 0; | ||
1051 | dvbdmxfeed->filter = NULL; | ||
1052 | dvbdmxfeed->buffer = NULL; | ||
1053 | |||
1054 | (*feed) = &dvbdmxfeed->feed.sec; | ||
1055 | (*feed)->is_filtering = 0; | ||
1056 | (*feed)->parent = demux; | ||
1057 | (*feed)->priv = NULL; | ||
1058 | |||
1059 | (*feed)->set = dmx_section_feed_set; | ||
1060 | (*feed)->allocate_filter = dmx_section_feed_allocate_filter; | ||
1061 | (*feed)->start_filtering = dmx_section_feed_start_filtering; | ||
1062 | (*feed)->stop_filtering = dmx_section_feed_stop_filtering; | ||
1063 | (*feed)->release_filter = dmx_section_feed_release_filter; | ||
1064 | |||
1065 | mutex_unlock(&dvbdmx->mutex); | ||
1066 | return 0; | ||
1067 | } | ||
1068 | |||
1069 | static int dvbdmx_release_section_feed(struct dmx_demux *demux, | ||
1070 | struct dmx_section_feed *feed) | ||
1071 | { | ||
1072 | struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; | ||
1073 | struct dvb_demux *dvbdmx = (struct dvb_demux *)demux; | ||
1074 | |||
1075 | mutex_lock(&dvbdmx->mutex); | ||
1076 | |||
1077 | if (dvbdmxfeed->state == DMX_STATE_FREE) { | ||
1078 | mutex_unlock(&dvbdmx->mutex); | ||
1079 | return -EINVAL; | ||
1080 | } | ||
1081 | #ifndef NOBUFS | ||
1082 | vfree(dvbdmxfeed->buffer); | ||
1083 | dvbdmxfeed->buffer = NULL; | ||
1084 | #endif | ||
1085 | dvbdmxfeed->state = DMX_STATE_FREE; | ||
1086 | |||
1087 | dvb_demux_feed_del(dvbdmxfeed); | ||
1088 | |||
1089 | dvbdmxfeed->pid = 0xffff; | ||
1090 | |||
1091 | mutex_unlock(&dvbdmx->mutex); | ||
1092 | return 0; | ||
1093 | } | ||
1094 | |||
1095 | /****************************************************************************** | ||
1096 | * dvb_demux kernel data API calls | ||
1097 | ******************************************************************************/ | ||
1098 | |||
1099 | static int dvbdmx_open(struct dmx_demux *demux) | ||
1100 | { | ||
1101 | struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; | ||
1102 | |||
1103 | if (dvbdemux->users >= MAX_DVB_DEMUX_USERS) | ||
1104 | return -EUSERS; | ||
1105 | |||
1106 | dvbdemux->users++; | ||
1107 | return 0; | ||
1108 | } | ||
1109 | |||
1110 | static int dvbdmx_close(struct dmx_demux *demux) | ||
1111 | { | ||
1112 | struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; | ||
1113 | |||
1114 | if (dvbdemux->users == 0) | ||
1115 | return -ENODEV; | ||
1116 | |||
1117 | dvbdemux->users--; | ||
1118 | //FIXME: release any unneeded resources if users==0 | ||
1119 | return 0; | ||
1120 | } | ||
1121 | |||
1122 | static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t count) | ||
1123 | { | ||
1124 | struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; | ||
1125 | void *p; | ||
1126 | |||
1127 | if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE)) | ||
1128 | return -EINVAL; | ||
1129 | |||
1130 | p = memdup_user(buf, count); | ||
1131 | if (IS_ERR(p)) | ||
1132 | return PTR_ERR(p); | ||
1133 | if (mutex_lock_interruptible(&dvbdemux->mutex)) { | ||
1134 | kfree(p); | ||
1135 | return -ERESTARTSYS; | ||
1136 | } | ||
1137 | dvb_dmx_swfilter(dvbdemux, p, count); | ||
1138 | kfree(p); | ||
1139 | mutex_unlock(&dvbdemux->mutex); | ||
1140 | |||
1141 | if (signal_pending(current)) | ||
1142 | return -EINTR; | ||
1143 | return count; | ||
1144 | } | ||
1145 | |||
1146 | static int dvbdmx_add_frontend(struct dmx_demux *demux, | ||
1147 | struct dmx_frontend *frontend) | ||
1148 | { | ||
1149 | struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; | ||
1150 | struct list_head *head = &dvbdemux->frontend_list; | ||
1151 | |||
1152 | list_add(&(frontend->connectivity_list), head); | ||
1153 | |||
1154 | return 0; | ||
1155 | } | ||
1156 | |||
1157 | static int dvbdmx_remove_frontend(struct dmx_demux *demux, | ||
1158 | struct dmx_frontend *frontend) | ||
1159 | { | ||
1160 | struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; | ||
1161 | struct list_head *pos, *n, *head = &dvbdemux->frontend_list; | ||
1162 | |||
1163 | list_for_each_safe(pos, n, head) { | ||
1164 | if (DMX_FE_ENTRY(pos) == frontend) { | ||
1165 | list_del(pos); | ||
1166 | return 0; | ||
1167 | } | ||
1168 | } | ||
1169 | |||
1170 | return -ENODEV; | ||
1171 | } | ||
1172 | |||
1173 | static struct list_head *dvbdmx_get_frontends(struct dmx_demux *demux) | ||
1174 | { | ||
1175 | struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; | ||
1176 | |||
1177 | if (list_empty(&dvbdemux->frontend_list)) | ||
1178 | return NULL; | ||
1179 | |||
1180 | return &dvbdemux->frontend_list; | ||
1181 | } | ||
1182 | |||
1183 | static int dvbdmx_connect_frontend(struct dmx_demux *demux, | ||
1184 | struct dmx_frontend *frontend) | ||
1185 | { | ||
1186 | struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; | ||
1187 | |||
1188 | if (demux->frontend) | ||
1189 | return -EINVAL; | ||
1190 | |||
1191 | mutex_lock(&dvbdemux->mutex); | ||
1192 | |||
1193 | demux->frontend = frontend; | ||
1194 | mutex_unlock(&dvbdemux->mutex); | ||
1195 | return 0; | ||
1196 | } | ||
1197 | |||
1198 | static int dvbdmx_disconnect_frontend(struct dmx_demux *demux) | ||
1199 | { | ||
1200 | struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; | ||
1201 | |||
1202 | mutex_lock(&dvbdemux->mutex); | ||
1203 | |||
1204 | demux->frontend = NULL; | ||
1205 | mutex_unlock(&dvbdemux->mutex); | ||
1206 | return 0; | ||
1207 | } | ||
1208 | |||
1209 | static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 * pids) | ||
1210 | { | ||
1211 | struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; | ||
1212 | |||
1213 | memcpy(pids, dvbdemux->pids, 5 * sizeof(u16)); | ||
1214 | return 0; | ||
1215 | } | ||
1216 | |||
1217 | int dvb_dmx_init(struct dvb_demux *dvbdemux) | ||
1218 | { | ||
1219 | int i; | ||
1220 | struct dmx_demux *dmx = &dvbdemux->dmx; | ||
1221 | |||
1222 | dvbdemux->cnt_storage = NULL; | ||
1223 | dvbdemux->users = 0; | ||
1224 | dvbdemux->filter = vmalloc(dvbdemux->filternum * sizeof(struct dvb_demux_filter)); | ||
1225 | |||
1226 | if (!dvbdemux->filter) | ||
1227 | return -ENOMEM; | ||
1228 | |||
1229 | dvbdemux->feed = vmalloc(dvbdemux->feednum * sizeof(struct dvb_demux_feed)); | ||
1230 | if (!dvbdemux->feed) { | ||
1231 | vfree(dvbdemux->filter); | ||
1232 | dvbdemux->filter = NULL; | ||
1233 | return -ENOMEM; | ||
1234 | } | ||
1235 | for (i = 0; i < dvbdemux->filternum; i++) { | ||
1236 | dvbdemux->filter[i].state = DMX_STATE_FREE; | ||
1237 | dvbdemux->filter[i].index = i; | ||
1238 | } | ||
1239 | for (i = 0; i < dvbdemux->feednum; i++) { | ||
1240 | dvbdemux->feed[i].state = DMX_STATE_FREE; | ||
1241 | dvbdemux->feed[i].index = i; | ||
1242 | } | ||
1243 | |||
1244 | dvbdemux->cnt_storage = vmalloc(MAX_PID + 1); | ||
1245 | if (!dvbdemux->cnt_storage) | ||
1246 | printk(KERN_WARNING "Couldn't allocate memory for TS/TEI check. Disabling it\n"); | ||
1247 | |||
1248 | INIT_LIST_HEAD(&dvbdemux->frontend_list); | ||
1249 | |||
1250 | for (i = 0; i < DMX_TS_PES_OTHER; i++) { | ||
1251 | dvbdemux->pesfilter[i] = NULL; | ||
1252 | dvbdemux->pids[i] = 0xffff; | ||
1253 | } | ||
1254 | |||
1255 | INIT_LIST_HEAD(&dvbdemux->feed_list); | ||
1256 | |||
1257 | dvbdemux->playing = 0; | ||
1258 | dvbdemux->recording = 0; | ||
1259 | dvbdemux->tsbufp = 0; | ||
1260 | |||
1261 | if (!dvbdemux->check_crc32) | ||
1262 | dvbdemux->check_crc32 = dvb_dmx_crc32; | ||
1263 | |||
1264 | if (!dvbdemux->memcopy) | ||
1265 | dvbdemux->memcopy = dvb_dmx_memcopy; | ||
1266 | |||
1267 | dmx->frontend = NULL; | ||
1268 | dmx->priv = dvbdemux; | ||
1269 | dmx->open = dvbdmx_open; | ||
1270 | dmx->close = dvbdmx_close; | ||
1271 | dmx->write = dvbdmx_write; | ||
1272 | dmx->allocate_ts_feed = dvbdmx_allocate_ts_feed; | ||
1273 | dmx->release_ts_feed = dvbdmx_release_ts_feed; | ||
1274 | dmx->allocate_section_feed = dvbdmx_allocate_section_feed; | ||
1275 | dmx->release_section_feed = dvbdmx_release_section_feed; | ||
1276 | |||
1277 | dmx->add_frontend = dvbdmx_add_frontend; | ||
1278 | dmx->remove_frontend = dvbdmx_remove_frontend; | ||
1279 | dmx->get_frontends = dvbdmx_get_frontends; | ||
1280 | dmx->connect_frontend = dvbdmx_connect_frontend; | ||
1281 | dmx->disconnect_frontend = dvbdmx_disconnect_frontend; | ||
1282 | dmx->get_pes_pids = dvbdmx_get_pes_pids; | ||
1283 | |||
1284 | mutex_init(&dvbdemux->mutex); | ||
1285 | spin_lock_init(&dvbdemux->lock); | ||
1286 | |||
1287 | return 0; | ||
1288 | } | ||
1289 | |||
1290 | EXPORT_SYMBOL(dvb_dmx_init); | ||
1291 | |||
1292 | void dvb_dmx_release(struct dvb_demux *dvbdemux) | ||
1293 | { | ||
1294 | vfree(dvbdemux->cnt_storage); | ||
1295 | vfree(dvbdemux->filter); | ||
1296 | vfree(dvbdemux->feed); | ||
1297 | } | ||
1298 | |||
1299 | EXPORT_SYMBOL(dvb_dmx_release); | ||