diff options
-rw-r--r-- | drivers/media/dvb/dvb-core/dmxdev.c | 231 | ||||
-rw-r--r-- | drivers/media/dvb/dvb-core/dmxdev.h | 9 | ||||
-rw-r--r-- | include/linux/dvb/dmx.h | 2 |
3 files changed, 184 insertions, 58 deletions
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c index 6d6121eb5d59..3750ff48cba1 100644 --- a/drivers/media/dvb/dvb-core/dmxdev.c +++ b/drivers/media/dvb/dvb-core/dmxdev.c | |||
@@ -430,6 +430,8 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, | |||
430 | /* stop feed but only mark the specified filter as stopped (state set) */ | 430 | /* stop feed but only mark the specified filter as stopped (state set) */ |
431 | static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter) | 431 | static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter) |
432 | { | 432 | { |
433 | struct dmxdev_feed *feed; | ||
434 | |||
433 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); | 435 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); |
434 | 436 | ||
435 | switch (dmxdevfilter->type) { | 437 | switch (dmxdevfilter->type) { |
@@ -438,7 +440,8 @@ static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter) | |||
438 | dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec); | 440 | dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec); |
439 | break; | 441 | break; |
440 | case DMXDEV_TYPE_PES: | 442 | case DMXDEV_TYPE_PES: |
441 | dmxdevfilter->feed.ts->stop_filtering(dmxdevfilter->feed.ts); | 443 | list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) |
444 | feed->ts->stop_filtering(feed->ts); | ||
442 | break; | 445 | break; |
443 | default: | 446 | default: |
444 | return -EINVAL; | 447 | return -EINVAL; |
@@ -449,13 +452,23 @@ static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter) | |||
449 | /* start feed associated with the specified filter */ | 452 | /* start feed associated with the specified filter */ |
450 | static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter) | 453 | static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter) |
451 | { | 454 | { |
455 | struct dmxdev_feed *feed; | ||
456 | int ret; | ||
457 | |||
452 | dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO); | 458 | dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO); |
453 | 459 | ||
454 | switch (filter->type) { | 460 | switch (filter->type) { |
455 | case DMXDEV_TYPE_SEC: | 461 | case DMXDEV_TYPE_SEC: |
456 | return filter->feed.sec->start_filtering(filter->feed.sec); | 462 | return filter->feed.sec->start_filtering(filter->feed.sec); |
457 | case DMXDEV_TYPE_PES: | 463 | case DMXDEV_TYPE_PES: |
458 | return filter->feed.ts->start_filtering(filter->feed.ts); | 464 | list_for_each_entry(feed, &filter->feed.ts, next) { |
465 | ret = feed->ts->start_filtering(feed->ts); | ||
466 | if (ret < 0) { | ||
467 | dvb_dmxdev_feed_stop(filter); | ||
468 | return ret; | ||
469 | } | ||
470 | } | ||
471 | break; | ||
459 | default: | 472 | default: |
460 | return -EINVAL; | 473 | return -EINVAL; |
461 | } | 474 | } |
@@ -487,6 +500,9 @@ static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter) | |||
487 | 500 | ||
488 | static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter) | 501 | static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter) |
489 | { | 502 | { |
503 | struct dmxdev_feed *feed; | ||
504 | struct dmx_demux *demux; | ||
505 | |||
490 | if (dmxdevfilter->state < DMXDEV_STATE_GO) | 506 | if (dmxdevfilter->state < DMXDEV_STATE_GO) |
491 | return 0; | 507 | return 0; |
492 | 508 | ||
@@ -503,13 +519,12 @@ static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter) | |||
503 | dmxdevfilter->feed.sec = NULL; | 519 | dmxdevfilter->feed.sec = NULL; |
504 | break; | 520 | break; |
505 | case DMXDEV_TYPE_PES: | 521 | case DMXDEV_TYPE_PES: |
506 | if (!dmxdevfilter->feed.ts) | ||
507 | break; | ||
508 | dvb_dmxdev_feed_stop(dmxdevfilter); | 522 | dvb_dmxdev_feed_stop(dmxdevfilter); |
509 | dmxdevfilter->dev->demux-> | 523 | demux = dmxdevfilter->dev->demux; |
510 | release_ts_feed(dmxdevfilter->dev->demux, | 524 | list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) { |
511 | dmxdevfilter->feed.ts); | 525 | demux->release_ts_feed(demux, feed->ts); |
512 | dmxdevfilter->feed.ts = NULL; | 526 | feed->ts = NULL; |
527 | } | ||
513 | break; | 528 | break; |
514 | default: | 529 | default: |
515 | if (dmxdevfilter->state == DMXDEV_STATE_ALLOCATED) | 530 | if (dmxdevfilter->state == DMXDEV_STATE_ALLOCATED) |
@@ -521,19 +536,88 @@ static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter) | |||
521 | return 0; | 536 | return 0; |
522 | } | 537 | } |
523 | 538 | ||
539 | static void dvb_dmxdev_delete_pids(struct dmxdev_filter *dmxdevfilter) | ||
540 | { | ||
541 | struct dmxdev_feed *feed, *tmp; | ||
542 | |||
543 | /* delete all PIDs */ | ||
544 | list_for_each_entry_safe(feed, tmp, &dmxdevfilter->feed.ts, next) { | ||
545 | list_del(&feed->next); | ||
546 | kfree(feed); | ||
547 | } | ||
548 | |||
549 | BUG_ON(!list_empty(&dmxdevfilter->feed.ts)); | ||
550 | } | ||
551 | |||
524 | static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter) | 552 | static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter) |
525 | { | 553 | { |
526 | if (dmxdevfilter->state < DMXDEV_STATE_SET) | 554 | if (dmxdevfilter->state < DMXDEV_STATE_SET) |
527 | return 0; | 555 | return 0; |
528 | 556 | ||
557 | if (dmxdevfilter->type == DMXDEV_TYPE_PES) | ||
558 | dvb_dmxdev_delete_pids(dmxdevfilter); | ||
559 | |||
529 | dmxdevfilter->type = DMXDEV_TYPE_NONE; | 560 | dmxdevfilter->type = DMXDEV_TYPE_NONE; |
530 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED); | 561 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED); |
531 | return 0; | 562 | return 0; |
532 | } | 563 | } |
533 | 564 | ||
565 | static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev, | ||
566 | struct dmxdev_filter *filter, | ||
567 | struct dmxdev_feed *feed) | ||
568 | { | ||
569 | struct timespec timeout = { 0 }; | ||
570 | struct dmx_pes_filter_params *para = &filter->params.pes; | ||
571 | dmx_output_t otype; | ||
572 | int ret; | ||
573 | int ts_type; | ||
574 | enum dmx_ts_pes ts_pes; | ||
575 | struct dmx_ts_feed *tsfeed; | ||
576 | |||
577 | feed->ts = NULL; | ||
578 | otype = para->output; | ||
579 | |||
580 | ts_pes = (enum dmx_ts_pes)para->pes_type; | ||
581 | |||
582 | if (ts_pes < DMX_PES_OTHER) | ||
583 | ts_type = TS_DECODER; | ||
584 | else | ||
585 | ts_type = 0; | ||
586 | |||
587 | if (otype == DMX_OUT_TS_TAP) | ||
588 | ts_type |= TS_PACKET; | ||
589 | else if (otype == DMX_OUT_TSDEMUX_TAP) | ||
590 | ts_type |= TS_PACKET | TS_DEMUX; | ||
591 | else if (otype == DMX_OUT_TAP) | ||
592 | ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY; | ||
593 | |||
594 | ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux, &feed->ts, | ||
595 | dvb_dmxdev_ts_callback); | ||
596 | if (ret < 0) | ||
597 | return ret; | ||
598 | |||
599 | tsfeed = feed->ts; | ||
600 | tsfeed->priv = filter; | ||
601 | |||
602 | ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, 32768, timeout); | ||
603 | if (ret < 0) { | ||
604 | dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed); | ||
605 | return ret; | ||
606 | } | ||
607 | |||
608 | ret = tsfeed->start_filtering(tsfeed); | ||
609 | if (ret < 0) { | ||
610 | dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed); | ||
611 | return ret; | ||
612 | } | ||
613 | |||
614 | return 0; | ||
615 | } | ||
616 | |||
534 | static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter) | 617 | static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter) |
535 | { | 618 | { |
536 | struct dmxdev *dmxdev = filter->dev; | 619 | struct dmxdev *dmxdev = filter->dev; |
620 | struct dmxdev_feed *feed; | ||
537 | void *mem; | 621 | void *mem; |
538 | int ret, i; | 622 | int ret, i; |
539 | 623 | ||
@@ -631,56 +715,14 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter) | |||
631 | break; | 715 | break; |
632 | } | 716 | } |
633 | case DMXDEV_TYPE_PES: | 717 | case DMXDEV_TYPE_PES: |
634 | { | 718 | list_for_each_entry(feed, &filter->feed.ts, next) { |
635 | struct timespec timeout = { 0 }; | 719 | ret = dvb_dmxdev_start_feed(dmxdev, filter, feed); |
636 | struct dmx_pes_filter_params *para = &filter->params.pes; | 720 | if (ret < 0) { |
637 | dmx_output_t otype; | 721 | dvb_dmxdev_filter_stop(filter); |
638 | int ts_type; | 722 | return ret; |
639 | enum dmx_ts_pes ts_pes; | 723 | } |
640 | struct dmx_ts_feed **tsfeed = &filter->feed.ts; | ||
641 | |||
642 | filter->feed.ts = NULL; | ||
643 | otype = para->output; | ||
644 | |||
645 | ts_pes = (enum dmx_ts_pes)para->pes_type; | ||
646 | |||
647 | if (ts_pes < DMX_PES_OTHER) | ||
648 | ts_type = TS_DECODER; | ||
649 | else | ||
650 | ts_type = 0; | ||
651 | |||
652 | if (otype == DMX_OUT_TS_TAP) | ||
653 | ts_type |= TS_PACKET; | ||
654 | else if (otype == DMX_OUT_TSDEMUX_TAP) | ||
655 | ts_type |= TS_PACKET | TS_DEMUX; | ||
656 | else if (otype == DMX_OUT_TAP) | ||
657 | ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY; | ||
658 | |||
659 | ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux, | ||
660 | tsfeed, | ||
661 | dvb_dmxdev_ts_callback); | ||
662 | if (ret < 0) | ||
663 | return ret; | ||
664 | |||
665 | (*tsfeed)->priv = filter; | ||
666 | |||
667 | ret = (*tsfeed)->set(*tsfeed, para->pid, ts_type, ts_pes, | ||
668 | 32768, timeout); | ||
669 | if (ret < 0) { | ||
670 | dmxdev->demux->release_ts_feed(dmxdev->demux, | ||
671 | *tsfeed); | ||
672 | return ret; | ||
673 | } | ||
674 | |||
675 | ret = filter->feed.ts->start_filtering(filter->feed.ts); | ||
676 | if (ret < 0) { | ||
677 | dmxdev->demux->release_ts_feed(dmxdev->demux, | ||
678 | *tsfeed); | ||
679 | return ret; | ||
680 | } | 724 | } |
681 | |||
682 | break; | 725 | break; |
683 | } | ||
684 | default: | 726 | default: |
685 | return -EINVAL; | 727 | return -EINVAL; |
686 | } | 728 | } |
@@ -718,7 +760,7 @@ static int dvb_demux_open(struct inode *inode, struct file *file) | |||
718 | dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192); | 760 | dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192); |
719 | dmxdevfilter->type = DMXDEV_TYPE_NONE; | 761 | dmxdevfilter->type = DMXDEV_TYPE_NONE; |
720 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED); | 762 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED); |
721 | dmxdevfilter->feed.ts = NULL; | 763 | INIT_LIST_HEAD(&dmxdevfilter->feed.ts); |
722 | init_timer(&dmxdevfilter->timer); | 764 | init_timer(&dmxdevfilter->timer); |
723 | 765 | ||
724 | dvbdev->users++; | 766 | dvbdev->users++; |
@@ -760,6 +802,55 @@ static inline void invert_mode(dmx_filter_t *filter) | |||
760 | filter->mode[i] ^= 0xff; | 802 | filter->mode[i] ^= 0xff; |
761 | } | 803 | } |
762 | 804 | ||
805 | static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev, | ||
806 | struct dmxdev_filter *filter, u16 pid) | ||
807 | { | ||
808 | struct dmxdev_feed *feed; | ||
809 | |||
810 | if ((filter->type != DMXDEV_TYPE_PES) || | ||
811 | (filter->state < DMXDEV_STATE_SET)) | ||
812 | return -EINVAL; | ||
813 | |||
814 | /* only TS packet filters may have multiple PIDs */ | ||
815 | if ((filter->params.pes.output != DMX_OUT_TSDEMUX_TAP) && | ||
816 | (!list_empty(&filter->feed.ts))) | ||
817 | return -EINVAL; | ||
818 | |||
819 | feed = kzalloc(sizeof(struct dmxdev_feed), GFP_KERNEL); | ||
820 | if (feed == NULL) | ||
821 | return -ENOMEM; | ||
822 | |||
823 | feed->pid = pid; | ||
824 | list_add(&feed->next, &filter->feed.ts); | ||
825 | |||
826 | if (filter->state >= DMXDEV_STATE_GO) | ||
827 | return dvb_dmxdev_start_feed(dmxdev, filter, feed); | ||
828 | |||
829 | return 0; | ||
830 | } | ||
831 | |||
832 | static int dvb_dmxdev_remove_pid(struct dmxdev *dmxdev, | ||
833 | struct dmxdev_filter *filter, u16 pid) | ||
834 | { | ||
835 | struct dmxdev_feed *feed, *tmp; | ||
836 | |||
837 | if ((filter->type != DMXDEV_TYPE_PES) || | ||
838 | (filter->state < DMXDEV_STATE_SET)) | ||
839 | return -EINVAL; | ||
840 | |||
841 | list_for_each_entry_safe(feed, tmp, &filter->feed.ts, next) { | ||
842 | if ((feed->pid == pid) && (feed->ts != NULL)) { | ||
843 | feed->ts->stop_filtering(feed->ts); | ||
844 | filter->dev->demux->release_ts_feed(filter->dev->demux, | ||
845 | feed->ts); | ||
846 | list_del(&feed->next); | ||
847 | kfree(feed); | ||
848 | } | ||
849 | } | ||
850 | |||
851 | return 0; | ||
852 | } | ||
853 | |||
763 | static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev, | 854 | static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev, |
764 | struct dmxdev_filter *dmxdevfilter, | 855 | struct dmxdev_filter *dmxdevfilter, |
765 | struct dmx_sct_filter_params *params) | 856 | struct dmx_sct_filter_params *params) |
@@ -784,7 +875,10 @@ static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev, | |||
784 | struct dmxdev_filter *dmxdevfilter, | 875 | struct dmxdev_filter *dmxdevfilter, |
785 | struct dmx_pes_filter_params *params) | 876 | struct dmx_pes_filter_params *params) |
786 | { | 877 | { |
878 | int ret; | ||
879 | |||
787 | dvb_dmxdev_filter_stop(dmxdevfilter); | 880 | dvb_dmxdev_filter_stop(dmxdevfilter); |
881 | dvb_dmxdev_filter_reset(dmxdevfilter); | ||
788 | 882 | ||
789 | if (params->pes_type > DMX_PES_OTHER || params->pes_type < 0) | 883 | if (params->pes_type > DMX_PES_OTHER || params->pes_type < 0) |
790 | return -EINVAL; | 884 | return -EINVAL; |
@@ -795,6 +889,11 @@ static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev, | |||
795 | 889 | ||
796 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); | 890 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); |
797 | 891 | ||
892 | ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, | ||
893 | dmxdevfilter->params.pes.pid); | ||
894 | if (ret < 0) | ||
895 | return ret; | ||
896 | |||
798 | if (params->flags & DMX_IMMEDIATE_START) | 897 | if (params->flags & DMX_IMMEDIATE_START) |
799 | return dvb_dmxdev_filter_start(dmxdevfilter); | 898 | return dvb_dmxdev_filter_start(dmxdevfilter); |
800 | 899 | ||
@@ -958,6 +1057,24 @@ static int dvb_demux_do_ioctl(struct inode *inode, struct file *file, | |||
958 | &((struct dmx_stc *)parg)->base); | 1057 | &((struct dmx_stc *)parg)->base); |
959 | break; | 1058 | break; |
960 | 1059 | ||
1060 | case DMX_ADD_PID: | ||
1061 | if (mutex_lock_interruptible(&dmxdevfilter->mutex)) { | ||
1062 | ret = -ERESTARTSYS; | ||
1063 | break; | ||
1064 | } | ||
1065 | ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, *(u16 *)parg); | ||
1066 | mutex_unlock(&dmxdevfilter->mutex); | ||
1067 | break; | ||
1068 | |||
1069 | case DMX_REMOVE_PID: | ||
1070 | if (mutex_lock_interruptible(&dmxdevfilter->mutex)) { | ||
1071 | ret = -ERESTARTSYS; | ||
1072 | break; | ||
1073 | } | ||
1074 | ret = dvb_dmxdev_remove_pid(dmxdev, dmxdevfilter, *(u16 *)parg); | ||
1075 | mutex_unlock(&dmxdevfilter->mutex); | ||
1076 | break; | ||
1077 | |||
961 | default: | 1078 | default: |
962 | ret = -EINVAL; | 1079 | ret = -EINVAL; |
963 | break; | 1080 | break; |
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h index 29746e70d325..c1379b56dfb4 100644 --- a/drivers/media/dvb/dvb-core/dmxdev.h +++ b/drivers/media/dvb/dvb-core/dmxdev.h | |||
@@ -53,13 +53,20 @@ enum dmxdev_state { | |||
53 | DMXDEV_STATE_TIMEDOUT | 53 | DMXDEV_STATE_TIMEDOUT |
54 | }; | 54 | }; |
55 | 55 | ||
56 | struct dmxdev_feed { | ||
57 | u16 pid; | ||
58 | struct dmx_ts_feed *ts; | ||
59 | struct list_head next; | ||
60 | }; | ||
61 | |||
56 | struct dmxdev_filter { | 62 | struct dmxdev_filter { |
57 | union { | 63 | union { |
58 | struct dmx_section_filter *sec; | 64 | struct dmx_section_filter *sec; |
59 | } filter; | 65 | } filter; |
60 | 66 | ||
61 | union { | 67 | union { |
62 | struct dmx_ts_feed *ts; | 68 | /* list of TS and PES feeds (struct dmxdev_feed) */ |
69 | struct list_head ts; | ||
63 | struct dmx_section_feed *sec; | 70 | struct dmx_section_feed *sec; |
64 | } feed; | 71 | } feed; |
65 | 72 | ||
diff --git a/include/linux/dvb/dmx.h b/include/linux/dvb/dmx.h index fef943738a24..f078f3ac82d4 100644 --- a/include/linux/dvb/dmx.h +++ b/include/linux/dvb/dmx.h | |||
@@ -151,5 +151,7 @@ struct dmx_stc { | |||
151 | #define DMX_GET_CAPS _IOR('o', 48, dmx_caps_t) | 151 | #define DMX_GET_CAPS _IOR('o', 48, dmx_caps_t) |
152 | #define DMX_SET_SOURCE _IOW('o', 49, dmx_source_t) | 152 | #define DMX_SET_SOURCE _IOW('o', 49, dmx_source_t) |
153 | #define DMX_GET_STC _IOWR('o', 50, struct dmx_stc) | 153 | #define DMX_GET_STC _IOWR('o', 50, struct dmx_stc) |
154 | #define DMX_ADD_PID _IOW('o', 51, __u16) | ||
155 | #define DMX_REMOVE_PID _IOW('o', 52, __u16) | ||
154 | 156 | ||
155 | #endif /*_DVBDMX_H_*/ | 157 | #endif /*_DVBDMX_H_*/ |