1 /*****************************************************************************
2 * en50221.c : implementation of the transport, session and applications
4 *****************************************************************************
5 * Copyright (C) 2004 the VideoLAN team
7 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8 * Based on code from libdvbci Copyright (C) 2000 Klaus Schmidinger
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
26 #include <vlc/input.h>
28 #include <sys/ioctl.h>
31 #include <sys/types.h>
39 /* DVB Card Drivers */
40 #include <linux/dvb/version.h>
41 #include <linux/dvb/dmx.h>
42 #include <linux/dvb/frontend.h>
43 #include <linux/dvb/ca.h>
45 /* Include dvbpsi headers */
46 #ifdef HAVE_DVBPSI_DR_H
47 # include <dvbpsi/dvbpsi.h>
48 # include <dvbpsi/descriptor.h>
49 # include <dvbpsi/pat.h>
50 # include <dvbpsi/pmt.h>
51 # include <dvbpsi/dr.h>
52 # include <dvbpsi/psi.h>
55 # include "descriptor.h"
56 # include "tables/pat.h"
57 # include "tables/pmt.h"
58 # include "descriptors/dr.h"
65 #define HLCI_WAIT_CAM_READY 0
66 #define CAM_PROG_MAX MAX_PROGRAMS
68 static void ResourceManagerOpen( access_t * p_access, int i_session_id );
69 static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
70 static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
71 static void DateTimeOpen( access_t * p_access, int i_session_id );
72 static void MMIOpen( access_t * p_access, int i_session_id );
74 /*****************************************************************************
76 *****************************************************************************/
77 #define SIZE_INDICATOR 0x80
79 static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
81 *pi_length = *p_data++;
83 if ( (*pi_length & SIZE_INDICATOR) != 0 )
85 int l = *pi_length & ~SIZE_INDICATOR;
89 for ( i = 0; i < l; i++ )
90 *pi_length = (*pi_length << 8) | *p_data++;
96 static uint8_t *SetLength( uint8_t *p_data, int i_length )
100 if ( i_length < 128 )
104 else if ( i_length < 256 )
106 *p++ = SIZE_INDICATOR | 0x1;
109 else if ( i_length < 65536 )
111 *p++ = SIZE_INDICATOR | 0x2;
112 *p++ = i_length >> 8;
113 *p++ = i_length & 0xff;
115 else if ( i_length < 16777216 )
117 *p++ = SIZE_INDICATOR | 0x3;
118 *p++ = i_length >> 16;
119 *p++ = (i_length >> 8) & 0xff;
120 *p++ = i_length & 0xff;
124 *p++ = SIZE_INDICATOR | 0x4;
125 *p++ = i_length >> 24;
126 *p++ = (i_length >> 16) & 0xff;
127 *p++ = (i_length >> 8) & 0xff;
128 *p++ = i_length & 0xff;
139 #define MAX_TPDU_SIZE 2048
140 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
142 #define DATA_INDICATOR 0x80
146 #define T_CREATE_TC 0x82
147 #define T_CTC_REPLY 0x83
148 #define T_DELETE_TC 0x84
149 #define T_DTC_REPLY 0x85
150 #define T_REQUEST_TC 0x86
151 #define T_NEW_TC 0x87
152 #define T_TC_ERROR 0x88
153 #define T_DATA_LAST 0xA0
154 #define T_DATA_MORE 0xA1
156 static void Dump( vlc_bool_t b_outgoing, uint8_t *p_data, int i_size )
161 fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
162 for ( i = 0; i < i_size && i < MAX_DUMP; i++)
163 fprintf(stderr, "%02X ", p_data[i]);
164 fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
168 /*****************************************************************************
170 *****************************************************************************/
171 static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
172 const uint8_t *p_content, int i_length )
174 access_sys_t *p_sys = p_access->p_sys;
175 uint8_t i_tcid = i_slot + 1;
176 uint8_t p_data[MAX_TPDU_SIZE];
192 p_data[3] = 1; /* length */
199 p_data[3] = 2; /* length */
201 p_data[5] = p_content[0];
208 /* i_length <= MAX_TPDU_DATA */
209 uint8_t *p = p_data + 3;
210 p = SetLength( p, i_length + 1 );
214 memcpy( p, p_content, i_length );
215 i_size = i_length + (p - p_data);
222 Dump( VLC_TRUE, p_data, i_size );
224 if ( write( p_sys->i_ca_handle, p_data, i_size ) != i_size )
226 msg_Err( p_access, "cannot write to CAM device (%s)",
235 /*****************************************************************************
237 *****************************************************************************/
238 #define CAM_READ_TIMEOUT 3500 // ms
240 static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
241 uint8_t *p_data, int *pi_size )
243 access_sys_t *p_sys = p_access->p_sys;
244 uint8_t i_tcid = i_slot + 1;
246 struct pollfd pfd[1];
248 pfd[0].fd = p_sys->i_ca_handle;
249 pfd[0].events = POLLIN;
250 if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
252 msg_Err( p_access, "cannot poll from CAM device" );
256 if ( pi_size == NULL )
258 p_data = malloc( MAX_TPDU_SIZE );
263 i_size = read( p_sys->i_ca_handle, p_data, MAX_TPDU_SIZE );
265 if ( i_size >= 0 || errno != EINTR )
271 msg_Err( p_access, "cannot read from CAM device (%d:%s)", i_size,
276 if ( p_data[1] != i_tcid )
278 msg_Err( p_access, "invalid read from CAM device (%d instead of %d)",
284 p_sys->pb_tc_has_data[i_slot] = (i_size >= 4
285 && p_data[i_size - 4] == T_SB
286 && p_data[i_size - 3] == 2
287 && (p_data[i_size - 1] & DATA_INDICATOR))
288 ? VLC_TRUE : VLC_FALSE;
290 Dump( VLC_FALSE, p_data, i_size );
292 if ( pi_size == NULL )
305 #define ST_SESSION_NUMBER 0x90
306 #define ST_OPEN_SESSION_REQUEST 0x91
307 #define ST_OPEN_SESSION_RESPONSE 0x92
308 #define ST_CREATE_SESSION 0x93
309 #define ST_CREATE_SESSION_RESPONSE 0x94
310 #define ST_CLOSE_SESSION_REQUEST 0x95
311 #define ST_CLOSE_SESSION_RESPONSE 0x96
314 #define SS_NOT_ALLOCATED 0xF0
316 #define RI_RESOURCE_MANAGER 0x00010041
317 #define RI_APPLICATION_INFORMATION 0x00020041
318 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
319 #define RI_HOST_CONTROL 0x00200041
320 #define RI_DATE_TIME 0x00240041
321 #define RI_MMI 0x00400041
323 static int ResourceIdToInt( uint8_t *p_data )
325 return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
326 | ((int)p_data[2] << 8) | p_data[3];
329 /*****************************************************************************
331 *****************************************************************************/
332 static int SPDUSend( access_t * p_access, int i_session_id,
333 uint8_t *p_data, int i_size )
335 access_sys_t *p_sys = p_access->p_sys;
336 uint8_t *p_spdu = malloc( i_size + 4 );
339 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
341 *p++ = ST_SESSION_NUMBER;
343 *p++ = (i_session_id >> 8);
344 *p++ = i_session_id & 0xff;
346 memcpy( p, p_data, i_size );
353 if ( i_size > MAX_TPDU_DATA )
355 if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
356 MAX_TPDU_DATA ) != VLC_SUCCESS )
358 msg_Err( p_access, "couldn't send TPDU on session %d",
364 i_size -= MAX_TPDU_DATA;
368 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
371 msg_Err( p_access, "couldn't send TPDU on session %d",
379 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
382 msg_Err( p_access, "couldn't recv TPDU on session %d",
393 /*****************************************************************************
395 *****************************************************************************/
396 static void SessionOpen( access_t * p_access, uint8_t i_slot,
397 uint8_t *p_spdu, int i_size )
399 access_sys_t *p_sys = p_access->p_sys;
401 int i_resource_id = ResourceIdToInt( &p_spdu[2] );
402 uint8_t p_response[16];
403 int i_status = SS_NOT_ALLOCATED;
406 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
408 if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
411 if ( i_session_id == MAX_SESSIONS )
413 msg_Err( p_access, "too many sessions !" );
416 p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
417 p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
418 p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
419 p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
421 if ( i_resource_id == RI_RESOURCE_MANAGER
422 || i_resource_id == RI_APPLICATION_INFORMATION
423 || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
424 || i_resource_id == RI_DATE_TIME
425 || i_resource_id == RI_MMI )
430 p_response[0] = ST_OPEN_SESSION_RESPONSE;
432 p_response[2] = i_status;
433 p_response[3] = p_spdu[2];
434 p_response[4] = p_spdu[3];
435 p_response[5] = p_spdu[4];
436 p_response[6] = p_spdu[5];
437 p_response[7] = i_session_id >> 8;
438 p_response[8] = i_session_id & 0xff;
440 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) !=
444 "SessionOpen: couldn't send TPDU on slot %d", i_slot );
447 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
450 "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
454 switch ( i_resource_id )
456 case RI_RESOURCE_MANAGER:
457 ResourceManagerOpen( p_access, i_session_id ); break;
458 case RI_APPLICATION_INFORMATION:
459 ApplicationInformationOpen( p_access, i_session_id ); break;
460 case RI_CONDITIONAL_ACCESS_SUPPORT:
461 ConditionalAccessOpen( p_access, i_session_id ); break;
463 DateTimeOpen( p_access, i_session_id ); break;
465 MMIOpen( p_access, i_session_id ); break;
467 case RI_HOST_CONTROL:
469 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
470 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
474 /*****************************************************************************
476 *****************************************************************************/
477 static void SessionSendClose( access_t * p_access, int i_session_id )
479 access_sys_t *p_sys = p_access->p_sys;
480 uint8_t p_response[16];
482 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
484 p_response[0] = ST_CLOSE_SESSION_REQUEST;
486 p_response[2] = i_session_id >> 8;
487 p_response[3] = i_session_id & 0xff;
489 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
493 "SessionSendClose: couldn't send TPDU on slot %d", i_slot );
496 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
499 "SessionSendClose: couldn't recv TPDU on slot %d", i_slot );
504 /*****************************************************************************
506 *****************************************************************************/
507 static void SessionClose( access_t * p_access, int i_session_id )
509 access_sys_t *p_sys = p_access->p_sys;
510 uint8_t p_response[16];
512 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
514 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
515 p_sys->p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
516 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
518 p_response[0] = ST_CLOSE_SESSION_RESPONSE;
520 p_response[2] = SS_OK;
521 p_response[3] = i_session_id >> 8;
522 p_response[4] = i_session_id & 0xff;
524 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
528 "SessionClose: couldn't send TPDU on slot %d", i_slot );
531 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
534 "SessionClose: couldn't recv TPDU on slot %d", i_slot );
539 /*****************************************************************************
541 *****************************************************************************/
542 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
543 uint8_t *p_spdu, int i_size )
545 access_sys_t *p_sys = p_access->p_sys;
550 case ST_SESSION_NUMBER:
553 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
554 p_sys->p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
555 p_spdu + 4, i_size - 4 );
558 case ST_OPEN_SESSION_REQUEST:
559 if ( i_size != 6 || p_spdu[1] != 0x4 )
561 SessionOpen( p_access, i_slot, p_spdu, i_size );
564 case ST_CLOSE_SESSION_REQUEST:
565 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
566 SessionClose( p_access, i_session_id );
569 case ST_CLOSE_SESSION_RESPONSE:
570 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
571 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
572 p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
574 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
587 #define AOT_NONE 0x000000
588 #define AOT_PROFILE_ENQ 0x9F8010
589 #define AOT_PROFILE 0x9F8011
590 #define AOT_PROFILE_CHANGE 0x9F8012
591 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
592 #define AOT_APPLICATION_INFO 0x9F8021
593 #define AOT_ENTER_MENU 0x9F8022
594 #define AOT_CA_INFO_ENQ 0x9F8030
595 #define AOT_CA_INFO 0x9F8031
596 #define AOT_CA_PMT 0x9F8032
597 #define AOT_CA_PMT_REPLY 0x9F8033
598 #define AOT_TUNE 0x9F8400
599 #define AOT_REPLACE 0x9F8401
600 #define AOT_CLEAR_REPLACE 0x9F8402
601 #define AOT_ASK_RELEASE 0x9F8403
602 #define AOT_DATE_TIME_ENQ 0x9F8440
603 #define AOT_DATE_TIME 0x9F8441
604 #define AOT_CLOSE_MMI 0x9F8800
605 #define AOT_DISPLAY_CONTROL 0x9F8801
606 #define AOT_DISPLAY_REPLY 0x9F8802
607 #define AOT_TEXT_LAST 0x9F8803
608 #define AOT_TEXT_MORE 0x9F8804
609 #define AOT_KEYPAD_CONTROL 0x9F8805
610 #define AOT_KEYPRESS 0x9F8806
611 #define AOT_ENQ 0x9F8807
612 #define AOT_ANSW 0x9F8808
613 #define AOT_MENU_LAST 0x9F8809
614 #define AOT_MENU_MORE 0x9F880A
615 #define AOT_MENU_ANSW 0x9F880B
616 #define AOT_LIST_LAST 0x9F880C
617 #define AOT_LIST_MORE 0x9F880D
618 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
619 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
620 #define AOT_DISPLAY_MESSAGE 0x9F8810
621 #define AOT_SCENE_END_MARK 0x9F8811
622 #define AOT_SCENE_DONE 0x9F8812
623 #define AOT_SCENE_CONTROL 0x9F8813
624 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
625 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
626 #define AOT_FLUSH_DOWNLOAD 0x9F8816
627 #define AOT_DOWNLOAD_REPLY 0x9F8817
628 #define AOT_COMMS_CMD 0x9F8C00
629 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
630 #define AOT_COMMS_REPLY 0x9F8C02
631 #define AOT_COMMS_SEND_LAST 0x9F8C03
632 #define AOT_COMMS_SEND_MORE 0x9F8C04
633 #define AOT_COMMS_RCV_LAST 0x9F8C05
634 #define AOT_COMMS_RCV_MORE 0x9F8C06
636 /*****************************************************************************
638 *****************************************************************************/
639 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
644 for ( i = 0; i < 3; i++ )
645 t = (t << 8) | *p_apdu++;
652 /*****************************************************************************
654 *****************************************************************************/
655 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
657 return GetLength( &p_apdu[3], pi_size );
660 /*****************************************************************************
662 *****************************************************************************/
663 static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
664 uint8_t *p_data, int i_size )
666 access_sys_t *p_sys = p_access->p_sys;
667 uint8_t *p_apdu = malloc( i_size + 12 );
672 *p++ = (i_tag >> 16);
673 *p++ = (i_tag >> 8) & 0xff;
675 p = SetLength( p, i_size );
677 memcpy( p, p_data, i_size );
678 if( p_sys->i_ca_type == CA_CI_LINK )
680 i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
684 if( i_size + p - p_apdu >256 )
686 msg_Err( p_access, "CAM: apdu overflow" );
687 i_ret = VLC_EGENERIC;
692 ca_msg.length = i_size + p - p_apdu;
693 if( i_size == 0 ) ca_msg.length=3;
694 psz_hex = (char*)malloc( ca_msg.length*3 + 1);
695 memcpy( ca_msg.msg, p_apdu, i_size + p - p_apdu );
696 i_ret = ioctl(p_sys->i_ca_handle, CA_SEND_MSG, &ca_msg );
699 msg_Err( p_access, "Error sending to CAM: %s", strerror(errno) );
700 i_ret = VLC_EGENERIC;
712 /*****************************************************************************
713 * ResourceManagerHandle
714 *****************************************************************************/
715 static void ResourceManagerHandle( access_t * p_access, int i_session_id,
716 uint8_t *p_apdu, int i_size )
718 int i_tag = APDUGetTag( p_apdu, i_size );
722 case AOT_PROFILE_ENQ:
724 int resources[] = { htonl(RI_RESOURCE_MANAGER),
725 htonl(RI_APPLICATION_INFORMATION),
726 htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
730 APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
735 APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
739 msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
744 /*****************************************************************************
745 * ResourceManagerOpen
746 *****************************************************************************/
747 static void ResourceManagerOpen( access_t * p_access, int i_session_id )
749 access_sys_t *p_sys = p_access->p_sys;
751 msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
753 p_sys->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
755 APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
759 * Application Information
762 /*****************************************************************************
763 * ApplicationInformationHandle
764 *****************************************************************************/
765 static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
766 uint8_t *p_apdu, int i_size )
768 int i_tag = APDUGetTag( p_apdu, i_size );
772 case AOT_APPLICATION_INFO:
774 int i_type, i_manufacturer, i_code;
776 uint8_t *d = APDUGetLength( p_apdu, &l );
779 p_apdu[l + 4] = '\0';
782 i_manufacturer = ((int)d[0] << 8) | d[1];
784 i_code = ((int)d[0] << 8) | d[1];
786 d = GetLength( d, &l );
788 msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
789 d, i_type, i_manufacturer, i_code );
794 "unexpected tag in ApplicationInformationHandle (0x%x)",
799 /*****************************************************************************
800 * ApplicationInformationOpen
801 *****************************************************************************/
802 static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
804 access_sys_t *p_sys = p_access->p_sys;
806 msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
808 p_sys->p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
810 APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
817 #define MAX_CASYSTEM_IDS 16
821 uint16_t pi_system_ids[MAX_CASYSTEM_IDS + 1];
824 static vlc_bool_t CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
827 if( !p_ids ) return VLC_TRUE;
829 while ( p_ids->pi_system_ids[i] )
831 if ( p_ids->pi_system_ids[i] == i_id )
839 /*****************************************************************************
840 * CAPMTNeedsDescrambling
841 *****************************************************************************/
842 static vlc_bool_t CAPMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
844 dvbpsi_descriptor_t *p_dr;
845 dvbpsi_pmt_es_t *p_es;
847 for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
849 if( p_dr->i_tag == 0x9 )
855 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
857 for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
858 p_dr = p_dr->p_next )
860 if( p_dr->i_tag == 0x9 )
870 /*****************************************************************************
872 *****************************************************************************/
873 static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr )
877 while ( p_dr != NULL )
879 if( p_dr->i_tag == 0x9 )
881 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
883 if ( CheckSystemID( p_ids, i_sysid ) )
884 i_cad_size += p_dr->i_length + 2;
892 static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt,
893 uint16_t i_program_number, uint8_t i_version,
894 int i_size, dvbpsi_descriptor_t *p_dr,
900 p_data = malloc( 7 + i_size );
902 p_data = malloc( 6 );
904 p_data[0] = i_list_mgt;
905 p_data[1] = i_program_number >> 8;
906 p_data[2] = i_program_number & 0xff;
907 p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
913 p_data[4] = (i_size + 1) >> 8;
914 p_data[5] = (i_size + 1) & 0xff;
918 while ( p_dr != NULL )
920 if( p_dr->i_tag == 0x9 )
922 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
924 if ( CheckSystemID( p_ids, i_sysid ) )
927 p_data[i + 1] = p_dr->i_length;
928 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
929 // p_data[i+4] &= 0x1f;
930 i += p_dr->i_length + 2;
945 static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt,
946 int i_capmt_size, uint8_t i_type, uint16_t i_pid,
947 int i_size, dvbpsi_descriptor_t *p_dr,
954 p_data = realloc( p_capmt, i_capmt_size + 6 + i_size );
956 p_data = realloc( p_capmt, i_capmt_size + 5 );
961 p_data[i + 1] = i_pid >> 8;
962 p_data[i + 2] = i_pid & 0xff;
966 p_data[i + 3] = (i_size + 1) >> 8;
967 p_data[i + 4] = (i_size + 1) & 0xff;
968 p_data[i + 5] = i_cmd;
971 while ( p_dr != NULL )
973 if( p_dr->i_tag == 0x9 )
975 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
977 if ( CheckSystemID( p_ids, i_sysid ) )
980 p_data[i + 1] = p_dr->i_length;
981 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
982 i += p_dr->i_length + 2;
997 static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id,
998 dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt,
999 uint8_t i_cmd, int *pi_capmt_size )
1001 access_sys_t *p_sys = p_access->p_sys;
1002 system_ids_t *p_ids =
1003 (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1004 dvbpsi_pmt_es_t *p_es;
1005 int i_cad_size, i_cad_program_size;
1008 i_cad_size = i_cad_program_size =
1009 GetCADSize( p_ids, p_pmt->p_first_descriptor );
1010 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1012 i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor );
1018 "no compatible scrambling system for SID %d on session %d",
1019 p_pmt->i_program_number, i_session_id );
1024 p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number,
1025 p_pmt->i_version, i_cad_program_size,
1026 p_pmt->p_first_descriptor, i_cmd );
1028 if ( i_cad_program_size )
1029 *pi_capmt_size = 7 + i_cad_program_size;
1033 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1035 i_cad_size = GetCADSize( p_ids, p_es->p_first_descriptor );
1037 if ( i_cad_size || i_cad_program_size )
1039 p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type,
1040 p_es->i_pid, i_cad_size,
1041 p_es->p_first_descriptor, i_cmd );
1043 *pi_capmt_size += 6 + i_cad_size;
1045 *pi_capmt_size += 5;
1052 /*****************************************************************************
1054 *****************************************************************************/
1055 static void CAPMTFirst( access_t * p_access, int i_session_id,
1056 dvbpsi_pmt_t *p_pmt )
1061 msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d",
1062 p_pmt->i_program_number, i_session_id );
1064 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1065 0x3 /* only */, 0x1 /* ok_descrambling */,
1069 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1072 /*****************************************************************************
1074 *****************************************************************************/
1075 static void CAPMTAdd( access_t * p_access, int i_session_id,
1076 dvbpsi_pmt_t *p_pmt )
1081 if( p_access->p_sys->i_selected_programs >= CAM_PROG_MAX )
1083 msg_Warn( p_access, "Not adding CAPMT for SID %d, too many programs",
1084 p_pmt->i_program_number );
1087 p_access->p_sys->i_selected_programs++;
1088 if( p_access->p_sys->i_selected_programs == 1 )
1090 CAPMTFirst( p_access, i_session_id, p_pmt );
1095 msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
1096 p_pmt->i_program_number, i_session_id );
1098 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1099 0x4 /* add */, 0x1 /* ok_descrambling */,
1103 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1106 /*****************************************************************************
1108 *****************************************************************************/
1109 static void CAPMTUpdate( access_t * p_access, int i_session_id,
1110 dvbpsi_pmt_t *p_pmt )
1115 msg_Dbg( p_access, "updating CAPMT for SID %d on session %d",
1116 p_pmt->i_program_number, i_session_id );
1118 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1119 0x5 /* update */, 0x1 /* ok_descrambling */,
1123 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1126 /*****************************************************************************
1128 *****************************************************************************/
1129 static void CAPMTDelete( access_t * p_access, int i_session_id,
1130 dvbpsi_pmt_t *p_pmt )
1135 p_access->p_sys->i_selected_programs--;
1136 msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d",
1137 p_pmt->i_program_number, i_session_id );
1139 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1140 0x5 /* update */, 0x4 /* not selected */,
1144 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1147 /*****************************************************************************
1148 * ConditionalAccessHandle
1149 *****************************************************************************/
1150 static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
1151 uint8_t *p_apdu, int i_size )
1153 access_sys_t *p_sys = p_access->p_sys;
1154 system_ids_t *p_ids =
1155 (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1156 int i_tag = APDUGetTag( p_apdu, i_size );
1164 uint8_t *d = APDUGetLength( p_apdu, &l );
1165 msg_Dbg( p_access, "CA system IDs supported by the application :" );
1167 for ( i = 0; i < l / 2; i++ )
1169 p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
1171 msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] );
1173 p_ids->pi_system_ids[i] = 0;
1175 for ( i = 0; i < MAX_PROGRAMS; i++ )
1177 if ( p_sys->pp_selected_programs[i] != NULL )
1179 CAPMTAdd( p_access, i_session_id,
1180 p_sys->pp_selected_programs[i] );
1188 "unexpected tag in ConditionalAccessHandle (0x%x)",
1193 /*****************************************************************************
1194 * ConditionalAccessOpen
1195 *****************************************************************************/
1196 static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
1198 access_sys_t *p_sys = p_access->p_sys;
1200 msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
1202 p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
1203 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(system_ids_t));
1204 memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0,
1205 sizeof(system_ids_t) );
1207 APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
1220 /*****************************************************************************
1222 *****************************************************************************/
1223 static void DateTimeSend( access_t * p_access, int i_session_id )
1225 access_sys_t *p_sys = p_access->p_sys;
1226 date_time_t *p_date =
1227 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1229 time_t t = time(NULL);
1233 if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
1235 int Y = tm_gmt.tm_year;
1236 int M = tm_gmt.tm_mon + 1;
1237 int D = tm_gmt.tm_mday;
1238 int L = (M == 1 || M == 2) ? 1 : 0;
1239 int MJD = 14956 + D + (int)((Y - L) * 365.25)
1240 + (int)((M + 1 + L * 12) * 30.6001);
1241 uint8_t p_response[7];
1243 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
1245 p_response[0] = htons(MJD) >> 8;
1246 p_response[1] = htons(MJD) & 0xff;
1247 p_response[2] = DEC2BCD(tm_gmt.tm_hour);
1248 p_response[3] = DEC2BCD(tm_gmt.tm_min);
1249 p_response[4] = DEC2BCD(tm_gmt.tm_sec);
1250 p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
1251 p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
1253 APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
1255 p_date->i_last = mdate();
1259 /*****************************************************************************
1261 *****************************************************************************/
1262 static void DateTimeHandle( access_t * p_access, int i_session_id,
1263 uint8_t *p_apdu, int i_size )
1265 access_sys_t *p_sys = p_access->p_sys;
1266 date_time_t *p_date =
1267 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1269 int i_tag = APDUGetTag( p_apdu, i_size );
1273 case AOT_DATE_TIME_ENQ:
1276 const uint8_t *d = APDUGetLength( p_apdu, &l );
1280 p_date->i_interval = *d;
1281 msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
1282 p_date->i_interval );
1285 p_date->i_interval = 0;
1287 DateTimeSend( p_access, i_session_id );
1291 msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
1295 /*****************************************************************************
1297 *****************************************************************************/
1298 static void DateTimeManage( access_t * p_access, int i_session_id )
1300 access_sys_t *p_sys = p_access->p_sys;
1301 date_time_t *p_date =
1302 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1304 if ( p_date->i_interval
1305 && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
1307 DateTimeSend( p_access, i_session_id );
1311 /*****************************************************************************
1313 *****************************************************************************/
1314 static void DateTimeOpen( access_t * p_access, int i_session_id )
1316 access_sys_t *p_sys = p_access->p_sys;
1318 msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
1320 p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
1321 p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
1322 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t));
1323 memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
1325 DateTimeSend( p_access, i_session_id );
1332 /* Display Control Commands */
1334 #define DCC_SET_MMI_MODE 0x01
1335 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
1336 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
1337 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
1338 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
1342 #define MM_HIGH_LEVEL 0x01
1343 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
1344 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
1346 /* Display Reply IDs */
1348 #define DRI_MMI_MODE_ACK 0x01
1349 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
1350 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
1351 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
1352 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
1353 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
1354 #define DRI_UNKNOWN_MMI_MODE 0xF1
1355 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
1359 #define EF_BLIND 0x01
1363 #define AI_CANCEL 0x00
1364 #define AI_ANSWER 0x01
1366 /*****************************************************************************
1368 *****************************************************************************/
1369 static void MMIDisplayReply( access_t *p_access, int i_session_id )
1371 uint8_t p_response[2];
1373 p_response[0] = DRI_MMI_MODE_ACK;
1374 p_response[1] = MM_HIGH_LEVEL;
1376 APDUSend( p_access, i_session_id, AOT_DISPLAY_REPLY, p_response, 2 );
1378 msg_Dbg( p_access, "sending DisplayReply on session (%d)", i_session_id );
1381 /*****************************************************************************
1383 *****************************************************************************/
1384 static char *MMIGetText( access_t *p_access, char *psz_text,
1385 uint8_t **pp_apdu, int *pi_size )
1387 int i_tag = APDUGetTag( *pp_apdu, *pi_size );
1391 if ( i_tag != AOT_TEXT_LAST )
1393 msg_Err( p_access, "unexpected text tag: %06x", i_tag );
1399 d = APDUGetLength( *pp_apdu, &l );
1400 strncpy( psz_text, (char *)d, l );
1408 /*****************************************************************************
1410 *****************************************************************************/
1411 static void MMIHandle( access_t *p_access, int i_session_id,
1412 uint8_t *p_apdu, int i_size )
1414 int i_tag = APDUGetTag( p_apdu, i_size );
1418 case AOT_DISPLAY_CONTROL:
1421 uint8_t *d = APDUGetLength( p_apdu, &l );
1427 case DCC_SET_MMI_MODE:
1428 if ( l == 2 && d[1] == MM_HIGH_LEVEL )
1429 MMIDisplayReply( p_access, i_session_id );
1431 msg_Err( p_access, "unsupported MMI mode %02x", d[1] );
1435 msg_Err( p_access, "unsupported display control command %02x",
1447 uint8_t *d = APDUGetLength( p_apdu, &l );
1452 l--; d++; /* choice_nb */
1455 msg_Info( p_access, "MMI title: %s",
1456 MMIGetText( p_access, psz_text, &d, &l ) );
1458 msg_Info( p_access, "MMI subtitle: %s",
1459 MMIGetText( p_access, psz_text, &d, &l ) );
1461 msg_Info( p_access, "MMI bottom: %s",
1462 MMIGetText( p_access, psz_text, &d, &l ) );
1465 msg_Info( p_access, "MMI: %s",
1466 MMIGetText( p_access, psz_text, &d, &l ) );
1473 SessionSendClose( p_access, i_session_id );
1474 msg_Dbg( p_access, "closing MMI session (%d)", i_session_id );
1478 msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
1482 /*****************************************************************************
1484 *****************************************************************************/
1485 static void MMIOpen( access_t *p_access, int i_session_id )
1487 access_sys_t *p_sys = p_access->p_sys;
1489 msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
1491 p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
1499 /*****************************************************************************
1500 * InitSlot: Open the transport layer
1501 *****************************************************************************/
1502 #define MAX_TC_RETRIES 20
1504 static int InitSlot( access_t * p_access, int i_slot )
1506 access_sys_t *p_sys = p_access->p_sys;
1509 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1512 msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
1514 return VLC_EGENERIC;
1517 /* This is out of the spec */
1518 for ( i = 0; i < MAX_TC_RETRIES; i++ )
1521 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
1522 && i_tag == T_CTC_REPLY )
1524 p_sys->pb_active_slot[i_slot] = VLC_TRUE;
1528 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1532 "en50221_Init: couldn't send TPDU on slot %d",
1537 if ( p_sys->pb_active_slot[i_slot] )
1539 p_sys->i_ca_timeout = 100000;
1543 return VLC_EGENERIC;
1548 * External entry points
1550 /*****************************************************************************
1551 * en50221_Init : Initialize the CAM for en50221
1552 *****************************************************************************/
1553 int E_(en50221_Init)( access_t * p_access )
1555 access_sys_t *p_sys = p_access->p_sys;
1557 if( p_sys->i_ca_type & CA_CI_LINK )
1560 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1562 if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
1564 msg_Err( p_access, "en50221_Init: couldn't reset slot %d",
1569 p_sys->i_ca_timeout = 100000;
1570 /* Wait a bit otherwise it doesn't initialize properly... */
1577 struct ca_slot_info info;
1579 /* We don't reset the CAM in that case because it's done by the
1581 if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &info ) < 0 )
1583 msg_Err( p_access, "en50221_Init: couldn't get slot info" );
1584 close( p_sys->i_ca_handle );
1585 p_sys->i_ca_handle = 0;
1586 return VLC_EGENERIC;
1588 if( info.flags == 0 )
1590 msg_Err( p_access, "en50221_Init: no CAM inserted" );
1591 close( p_sys->i_ca_handle );
1592 p_sys->i_ca_handle = 0;
1593 return VLC_EGENERIC;
1596 /* Allocate a dummy sessions */
1597 p_sys->p_sessions[ 0 ].i_resource_id = RI_CONDITIONAL_ACCESS_SUPPORT;
1599 /* Get application info to find out which cam we are using and make
1600 sure everything is ready to play */
1603 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1604 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1605 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1606 memset( &ca_msg.msg[3], 0, 253 );
1607 APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1608 if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1610 msg_Err( p_access, "en50221_Init: failed getting message" );
1611 return VLC_EGENERIC;
1614 #if HLCI_WAIT_CAM_READY
1615 while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1617 if( p_access->b_die ) return VLC_EGENERIC;
1619 msg_Dbg( p_access, "CAM: please wait" );
1620 APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1622 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1623 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1624 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1625 memset( &ca_msg.msg[3], 0, 253 );
1626 if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1628 msg_Err( p_access, "en50221_Init: failed getting message" );
1629 return VLC_EGENERIC;
1631 msg_Dbg( p_access, "en50221_Init: Got length: %d, tag: 0x%x", ca_msg.length, APDUGetTag( ca_msg.msg, ca_msg.length ) );
1634 if( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1636 msg_Err( p_access, "CAM returns garbage as application info!" );
1637 return VLC_EGENERIC;
1640 msg_Dbg( p_access, "found CAM %s using id 0x%x", &ca_msg.msg[12],
1641 (ca_msg.msg[8]<<8)|ca_msg.msg[9] );
1646 /*****************************************************************************
1647 * en50221_Poll : Poll the CAM for TPDUs
1648 *****************************************************************************/
1649 int E_(en50221_Poll)( access_t * p_access )
1651 access_sys_t *p_sys = p_access->p_sys;
1655 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1659 if ( !p_sys->pb_active_slot[i_slot] )
1661 ca_slot_info_t sinfo;
1663 if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
1665 msg_Err( p_access, "en50221_Poll: couldn't get info on slot %d",
1670 if ( sinfo.flags & CA_CI_MODULE_READY )
1672 msg_Dbg( p_access, "en50221_Poll: slot %d is active",
1674 p_sys->pb_active_slot[i_slot] = VLC_TRUE;
1679 InitSlot( p_access, i_slot );
1682 if ( !p_sys->pb_tc_has_data[i_slot] )
1684 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, NULL, 0 ) !=
1688 "en50221_Poll: couldn't send TPDU on slot %d",
1692 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) !=
1696 "en50221_Poll: couldn't recv TPDU on slot %d",
1702 while ( p_sys->pb_tc_has_data[i_slot] )
1704 uint8_t p_tpdu[MAX_TPDU_SIZE];
1705 int i_size, i_session_size;
1708 if ( TPDUSend( p_access, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
1711 "en50221_Poll: couldn't send TPDU on slot %d",
1715 if ( TPDURecv( p_access, i_slot, &i_tag, p_tpdu, &i_size ) !=
1719 "en50221_Poll: couldn't recv TPDU on slot %d",
1724 p_session = GetLength( &p_tpdu[3], &i_session_size );
1725 if ( i_session_size <= 1 )
1731 if ( i_tag != T_DATA_LAST )
1734 "en50221_Poll: fragmented TPDU not supported" );
1738 SPDUHandle( p_access, i_slot, p_session, i_session_size );
1742 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
1744 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1745 && p_sys->p_sessions[i_session_id - 1].pf_manage )
1747 p_sys->p_sessions[i_session_id - 1].pf_manage( p_access,
1756 /*****************************************************************************
1757 * en50221_SetCAPMT :
1758 *****************************************************************************/
1759 int E_(en50221_SetCAPMT)( access_t * p_access, dvbpsi_pmt_t *p_pmt )
1761 access_sys_t *p_sys = p_access->p_sys;
1762 int i, i_session_id;
1763 vlc_bool_t b_update = VLC_FALSE;
1764 vlc_bool_t b_needs_descrambling = CAPMTNeedsDescrambling( p_pmt );
1766 for ( i = 0; i < MAX_PROGRAMS; i++ )
1768 if ( p_sys->pp_selected_programs[i] != NULL
1769 && p_sys->pp_selected_programs[i]->i_program_number
1770 == p_pmt->i_program_number )
1772 b_update = VLC_TRUE;
1774 if ( !b_needs_descrambling )
1776 dvbpsi_DeletePMT( p_pmt );
1777 p_pmt = p_sys->pp_selected_programs[i];
1778 p_sys->pp_selected_programs[i] = NULL;
1780 else if( p_pmt != p_sys->pp_selected_programs[i] )
1782 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
1783 p_sys->pp_selected_programs[i] = p_pmt;
1790 if ( !b_update && b_needs_descrambling )
1792 for ( i = 0; i < MAX_PROGRAMS; i++ )
1794 if ( p_sys->pp_selected_programs[i] == NULL )
1796 p_sys->pp_selected_programs[i] = p_pmt;
1802 if ( b_update || b_needs_descrambling )
1804 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
1806 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1807 == RI_CONDITIONAL_ACCESS_SUPPORT )
1809 if ( b_update && b_needs_descrambling )
1810 CAPMTUpdate( p_access, i_session_id, p_pmt );
1811 else if ( b_update )
1812 CAPMTDelete( p_access, i_session_id, p_pmt );
1814 CAPMTAdd( p_access, i_session_id, p_pmt );
1819 if ( !b_needs_descrambling )
1821 dvbpsi_DeletePMT( p_pmt );
1827 /*****************************************************************************
1829 *****************************************************************************/
1830 void E_(en50221_End)( access_t * p_access )
1832 access_sys_t *p_sys = p_access->p_sys;
1835 for ( i = 0; i < MAX_PROGRAMS; i++ )
1837 if ( p_sys->pp_selected_programs[i] != NULL )
1839 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );