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"
66 static void ResourceManagerOpen( access_t * p_access, int i_session_id );
67 static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
68 static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
69 static void DateTimeOpen( access_t * p_access, int i_session_id );
70 static void MMIOpen( access_t * p_access, int i_session_id );
72 /*****************************************************************************
74 *****************************************************************************/
75 #define SIZE_INDICATOR 0x80
77 static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
79 *pi_length = *p_data++;
81 if ( (*pi_length & SIZE_INDICATOR) != 0 )
83 int l = *pi_length & ~SIZE_INDICATOR;
87 for ( i = 0; i < l; i++ )
88 *pi_length = (*pi_length << 8) | *p_data++;
94 static uint8_t *SetLength( uint8_t *p_data, int i_length )
102 else if ( i_length < 256 )
104 *p++ = SIZE_INDICATOR | 0x1;
107 else if ( i_length < 65536 )
109 *p++ = SIZE_INDICATOR | 0x2;
110 *p++ = i_length >> 8;
111 *p++ = i_length & 0xff;
113 else if ( i_length < 16777216 )
115 *p++ = SIZE_INDICATOR | 0x3;
116 *p++ = i_length >> 16;
117 *p++ = (i_length >> 8) & 0xff;
118 *p++ = i_length & 0xff;
122 *p++ = SIZE_INDICATOR | 0x4;
123 *p++ = i_length >> 24;
124 *p++ = (i_length >> 16) & 0xff;
125 *p++ = (i_length >> 8) & 0xff;
126 *p++ = i_length & 0xff;
137 #define MAX_TPDU_SIZE 2048
138 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
140 #define DATA_INDICATOR 0x80
144 #define T_CREATE_TC 0x82
145 #define T_CTC_REPLY 0x83
146 #define T_DELETE_TC 0x84
147 #define T_DTC_REPLY 0x85
148 #define T_REQUEST_TC 0x86
149 #define T_NEW_TC 0x87
150 #define T_TC_ERROR 0x88
151 #define T_DATA_LAST 0xA0
152 #define T_DATA_MORE 0xA1
154 static void Dump( vlc_bool_t b_outgoing, uint8_t *p_data, int i_size )
159 fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
160 for ( i = 0; i < i_size && i < MAX_DUMP; i++)
161 fprintf(stderr, "%02X ", p_data[i]);
162 fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
166 /*****************************************************************************
168 *****************************************************************************/
169 static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
170 const uint8_t *p_content, int i_length )
172 access_sys_t *p_sys = p_access->p_sys;
173 uint8_t i_tcid = i_slot + 1;
174 uint8_t p_data[MAX_TPDU_SIZE];
190 p_data[3] = 1; /* length */
197 p_data[3] = 2; /* length */
199 p_data[5] = p_content[0];
206 /* i_length <= MAX_TPDU_DATA */
207 uint8_t *p = p_data + 3;
208 p = SetLength( p, i_length + 1 );
212 memcpy( p, p_content, i_length );
213 i_size = i_length + (p - p_data);
220 Dump( VLC_TRUE, p_data, i_size );
222 if ( write( p_sys->i_ca_handle, p_data, i_size ) != i_size )
224 msg_Err( p_access, "cannot write to CAM device (%s)",
233 /*****************************************************************************
235 *****************************************************************************/
236 #define CAM_READ_TIMEOUT 3500 // ms
238 static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
239 uint8_t *p_data, int *pi_size )
241 access_sys_t *p_sys = p_access->p_sys;
242 uint8_t i_tcid = i_slot + 1;
244 struct pollfd pfd[1];
246 pfd[0].fd = p_sys->i_ca_handle;
247 pfd[0].events = POLLIN;
248 if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
250 msg_Err( p_access, "cannot poll from CAM device" );
254 if ( pi_size == NULL )
256 p_data = malloc( MAX_TPDU_SIZE );
261 i_size = read( p_sys->i_ca_handle, p_data, MAX_TPDU_SIZE );
263 if ( i_size >= 0 || errno != EINTR )
269 msg_Err( p_access, "cannot read from CAM device (%d:%s)", i_size,
274 if ( p_data[1] != i_tcid )
276 msg_Err( p_access, "invalid read from CAM device (%d instead of %d)",
282 p_sys->pb_tc_has_data[i_slot] = (i_size >= 4
283 && p_data[i_size - 4] == T_SB
284 && p_data[i_size - 3] == 2
285 && (p_data[i_size - 1] & DATA_INDICATOR))
286 ? VLC_TRUE : VLC_FALSE;
288 Dump( VLC_FALSE, p_data, i_size );
290 if ( pi_size == NULL )
303 #define ST_SESSION_NUMBER 0x90
304 #define ST_OPEN_SESSION_REQUEST 0x91
305 #define ST_OPEN_SESSION_RESPONSE 0x92
306 #define ST_CREATE_SESSION 0x93
307 #define ST_CREATE_SESSION_RESPONSE 0x94
308 #define ST_CLOSE_SESSION_REQUEST 0x95
309 #define ST_CLOSE_SESSION_RESPONSE 0x96
312 #define SS_NOT_ALLOCATED 0xF0
314 #define RI_RESOURCE_MANAGER 0x00010041
315 #define RI_APPLICATION_INFORMATION 0x00020041
316 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
317 #define RI_HOST_CONTROL 0x00200041
318 #define RI_DATE_TIME 0x00240041
319 #define RI_MMI 0x00400041
321 static int ResourceIdToInt( uint8_t *p_data )
323 return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
324 | ((int)p_data[2] << 8) | p_data[3];
327 /*****************************************************************************
329 *****************************************************************************/
330 static int SPDUSend( access_t * p_access, int i_session_id,
331 uint8_t *p_data, int i_size )
333 access_sys_t *p_sys = p_access->p_sys;
334 uint8_t *p_spdu = malloc( i_size + 4 );
337 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
339 *p++ = ST_SESSION_NUMBER;
341 *p++ = (i_session_id >> 8);
342 *p++ = i_session_id & 0xff;
344 memcpy( p, p_data, i_size );
351 if ( i_size > MAX_TPDU_DATA )
353 if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
354 MAX_TPDU_DATA ) != VLC_SUCCESS )
356 msg_Err( p_access, "couldn't send TPDU on session %d",
362 i_size -= MAX_TPDU_DATA;
366 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
369 msg_Err( p_access, "couldn't send TPDU on session %d",
377 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
380 msg_Err( p_access, "couldn't recv TPDU on session %d",
391 /*****************************************************************************
393 *****************************************************************************/
394 static void SessionOpen( access_t * p_access, uint8_t i_slot,
395 uint8_t *p_spdu, int i_size )
397 access_sys_t *p_sys = p_access->p_sys;
399 int i_resource_id = ResourceIdToInt( &p_spdu[2] );
400 uint8_t p_response[16];
401 int i_status = SS_NOT_ALLOCATED;
404 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
406 if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
409 if ( i_session_id == MAX_SESSIONS )
411 msg_Err( p_access, "too many sessions !" );
414 p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
415 p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
416 p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
417 p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
419 if ( i_resource_id == RI_RESOURCE_MANAGER
420 || i_resource_id == RI_APPLICATION_INFORMATION
421 || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
422 || i_resource_id == RI_DATE_TIME
423 || i_resource_id == RI_MMI )
428 p_response[0] = ST_OPEN_SESSION_RESPONSE;
430 p_response[2] = i_status;
431 p_response[3] = p_spdu[2];
432 p_response[4] = p_spdu[3];
433 p_response[5] = p_spdu[4];
434 p_response[6] = p_spdu[5];
435 p_response[7] = i_session_id >> 8;
436 p_response[8] = i_session_id & 0xff;
438 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) !=
442 "SessionOpen: couldn't send TPDU on slot %d", i_slot );
445 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
448 "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
452 switch ( i_resource_id )
454 case RI_RESOURCE_MANAGER:
455 ResourceManagerOpen( p_access, i_session_id ); break;
456 case RI_APPLICATION_INFORMATION:
457 ApplicationInformationOpen( p_access, i_session_id ); break;
458 case RI_CONDITIONAL_ACCESS_SUPPORT:
459 ConditionalAccessOpen( p_access, i_session_id ); break;
461 DateTimeOpen( p_access, i_session_id ); break;
463 MMIOpen( p_access, i_session_id ); break;
465 case RI_HOST_CONTROL:
467 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
468 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
472 /*****************************************************************************
474 *****************************************************************************/
475 static void SessionClose( access_t * p_access, int i_session_id )
477 access_sys_t *p_sys = p_access->p_sys;
478 uint8_t p_response[16];
480 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
482 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
483 p_sys->p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
484 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
486 p_response[0] = ST_CLOSE_SESSION_RESPONSE;
488 p_response[2] = SS_OK;
489 p_response[3] = i_session_id >> 8;
490 p_response[4] = i_session_id & 0xff;
492 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
496 "SessionOpen: couldn't send TPDU on slot %d", i_slot );
499 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
502 "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
507 /*****************************************************************************
509 *****************************************************************************/
510 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
511 uint8_t *p_spdu, int i_size )
513 access_sys_t *p_sys = p_access->p_sys;
518 case ST_SESSION_NUMBER:
521 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
522 p_sys->p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
523 p_spdu + 4, i_size - 4 );
526 case ST_OPEN_SESSION_REQUEST:
527 if ( i_size != 6 || p_spdu[1] != 0x4 )
529 SessionOpen( p_access, i_slot, p_spdu, i_size );
532 case ST_CLOSE_SESSION_REQUEST:
533 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
534 SessionClose( p_access, i_session_id );
547 #define AOT_NONE 0x000000
548 #define AOT_PROFILE_ENQ 0x9F8010
549 #define AOT_PROFILE 0x9F8011
550 #define AOT_PROFILE_CHANGE 0x9F8012
551 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
552 #define AOT_APPLICATION_INFO 0x9F8021
553 #define AOT_ENTER_MENU 0x9F8022
554 #define AOT_CA_INFO_ENQ 0x9F8030
555 #define AOT_CA_INFO 0x9F8031
556 #define AOT_CA_PMT 0x9F8032
557 #define AOT_CA_PMT_REPLY 0x9F8033
558 #define AOT_TUNE 0x9F8400
559 #define AOT_REPLACE 0x9F8401
560 #define AOT_CLEAR_REPLACE 0x9F8402
561 #define AOT_ASK_RELEASE 0x9F8403
562 #define AOT_DATE_TIME_ENQ 0x9F8440
563 #define AOT_DATE_TIME 0x9F8441
564 #define AOT_CLOSE_MMI 0x9F8800
565 #define AOT_DISPLAY_CONTROL 0x9F8801
566 #define AOT_DISPLAY_REPLY 0x9F8802
567 #define AOT_TEXT_LAST 0x9F8803
568 #define AOT_TEXT_MORE 0x9F8804
569 #define AOT_KEYPAD_CONTROL 0x9F8805
570 #define AOT_KEYPRESS 0x9F8806
571 #define AOT_ENQ 0x9F8807
572 #define AOT_ANSW 0x9F8808
573 #define AOT_MENU_LAST 0x9F8809
574 #define AOT_MENU_MORE 0x9F880A
575 #define AOT_MENU_ANSW 0x9F880B
576 #define AOT_LIST_LAST 0x9F880C
577 #define AOT_LIST_MORE 0x9F880D
578 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
579 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
580 #define AOT_DISPLAY_MESSAGE 0x9F8810
581 #define AOT_SCENE_END_MARK 0x9F8811
582 #define AOT_SCENE_DONE 0x9F8812
583 #define AOT_SCENE_CONTROL 0x9F8813
584 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
585 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
586 #define AOT_FLUSH_DOWNLOAD 0x9F8816
587 #define AOT_DOWNLOAD_REPLY 0x9F8817
588 #define AOT_COMMS_CMD 0x9F8C00
589 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
590 #define AOT_COMMS_REPLY 0x9F8C02
591 #define AOT_COMMS_SEND_LAST 0x9F8C03
592 #define AOT_COMMS_SEND_MORE 0x9F8C04
593 #define AOT_COMMS_RCV_LAST 0x9F8C05
594 #define AOT_COMMS_RCV_MORE 0x9F8C06
596 /*****************************************************************************
598 *****************************************************************************/
599 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
604 for ( i = 0; i < 3; i++ )
605 t = (t << 8) | *p_apdu++;
612 /*****************************************************************************
614 *****************************************************************************/
615 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
617 return GetLength( &p_apdu[3], pi_size );
620 /*****************************************************************************
622 *****************************************************************************/
623 static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
624 uint8_t *p_data, int i_size )
626 uint8_t *p_apdu = malloc( i_size + 12 );
630 *p++ = (i_tag >> 16);
631 *p++ = (i_tag >> 8) & 0xff;
633 p = SetLength( p, i_size );
635 memcpy( p, p_data, i_size );
637 i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
646 /*****************************************************************************
647 * ResourceManagerHandle
648 *****************************************************************************/
649 static void ResourceManagerHandle( access_t * p_access, int i_session_id,
650 uint8_t *p_apdu, int i_size )
652 int i_tag = APDUGetTag( p_apdu, i_size );
656 case AOT_PROFILE_ENQ:
658 int resources[] = { htonl(RI_RESOURCE_MANAGER),
659 htonl(RI_APPLICATION_INFORMATION),
660 htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
664 APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
669 APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
673 msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
678 /*****************************************************************************
679 * ResourceManagerOpen
680 *****************************************************************************/
681 static void ResourceManagerOpen( access_t * p_access, int i_session_id )
683 access_sys_t *p_sys = p_access->p_sys;
685 msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
687 p_sys->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
689 APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
693 * Application Information
696 /*****************************************************************************
697 * ApplicationInformationHandle
698 *****************************************************************************/
699 static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
700 uint8_t *p_apdu, int i_size )
702 int i_tag = APDUGetTag( p_apdu, i_size );
706 case AOT_APPLICATION_INFO:
708 int i_type, i_manufacturer, i_code;
710 uint8_t *d = APDUGetLength( p_apdu, &l );
713 p_apdu[l + 4] = '\0';
716 i_manufacturer = ((int)d[0] << 8) | d[1];
718 i_code = ((int)d[0] << 8) | d[1];
720 d = GetLength( d, &l );
722 msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
723 d, i_type, i_manufacturer, i_code );
728 "unexpected tag in ApplicationInformationHandle (0x%x)",
733 /*****************************************************************************
734 * ApplicationInformationOpen
735 *****************************************************************************/
736 static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
738 access_sys_t *p_sys = p_access->p_sys;
740 msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
742 p_sys->p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
744 APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
751 #define MAX_CASYSTEM_IDS 16
755 uint16_t pi_system_ids[MAX_CASYSTEM_IDS + 1];
758 static vlc_bool_t CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
761 while ( p_ids->pi_system_ids[i] )
763 if ( p_ids->pi_system_ids[i] == i_id )
771 /*****************************************************************************
772 * CAPMTNeedsDescrambling
773 *****************************************************************************/
774 static vlc_bool_t CAPMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
776 dvbpsi_descriptor_t *p_dr;
777 dvbpsi_pmt_es_t *p_es;
779 for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
781 if( p_dr->i_tag == 0x9 )
787 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
789 for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
790 p_dr = p_dr->p_next )
792 if( p_dr->i_tag == 0x9 )
802 /*****************************************************************************
804 *****************************************************************************/
805 static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr )
809 while ( p_dr != NULL )
811 if( p_dr->i_tag == 0x9 )
813 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
815 if ( CheckSystemID( p_ids, i_sysid ) )
816 i_cad_size += p_dr->i_length + 2;
824 static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt,
825 uint16_t i_program_number, uint8_t i_version,
826 int i_size, dvbpsi_descriptor_t *p_dr,
832 p_data = malloc( 7 + i_size );
834 p_data = malloc( 6 );
836 p_data[0] = i_list_mgt;
837 p_data[1] = i_program_number >> 8;
838 p_data[2] = i_program_number & 0xff;
839 p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
845 p_data[4] = (i_size + 1) >> 8;
846 p_data[5] = (i_size + 1) & 0xff;
850 while ( p_dr != NULL )
852 if( p_dr->i_tag == 0x9 )
854 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
856 if ( CheckSystemID( p_ids, i_sysid ) )
859 p_data[i + 1] = p_dr->i_length;
860 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
861 i += p_dr->i_length + 2;
876 static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt,
877 int i_capmt_size, uint8_t i_type, uint16_t i_pid,
878 int i_size, dvbpsi_descriptor_t *p_dr,
885 p_data = realloc( p_capmt, i_capmt_size + 6 + i_size );
887 p_data = realloc( p_capmt, i_capmt_size + 5 );
892 p_data[i + 1] = i_pid >> 8;
893 p_data[i + 2] = i_pid & 0xff;
897 p_data[i + 3] = (i_size + 1) >> 8;
898 p_data[i + 4] = (i_size + 1) & 0xff;
899 p_data[i + 5] = i_cmd;
902 while ( p_dr != NULL )
904 if( p_dr->i_tag == 0x9 )
906 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
908 if ( CheckSystemID( p_ids, i_sysid ) )
911 p_data[i + 1] = p_dr->i_length;
912 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
913 i += p_dr->i_length + 2;
928 static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id,
929 dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt,
930 uint8_t i_cmd, int *pi_capmt_size )
932 access_sys_t *p_sys = p_access->p_sys;
933 system_ids_t *p_ids =
934 (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
935 dvbpsi_pmt_es_t *p_es;
936 int i_cad_size, i_cad_program_size;
939 i_cad_size = i_cad_program_size =
940 GetCADSize( p_ids, p_pmt->p_first_descriptor );
941 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
943 i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor );
949 "no compatible scrambling system for SID %d on session %d",
950 p_pmt->i_program_number, i_session_id );
955 p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number,
956 p_pmt->i_version, i_cad_program_size,
957 p_pmt->p_first_descriptor, i_cmd );
959 if ( i_cad_program_size )
960 *pi_capmt_size = 7 + i_cad_program_size;
964 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
966 i_cad_size = GetCADSize( p_ids, p_es->p_first_descriptor );
968 if ( i_cad_size || i_cad_program_size )
970 p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type,
971 p_es->i_pid, i_cad_size,
972 p_es->p_first_descriptor, i_cmd );
974 *pi_capmt_size += 6 + i_cad_size;
983 /*****************************************************************************
985 *****************************************************************************/
986 static void CAPMTFirst( access_t * p_access, int i_session_id,
987 dvbpsi_pmt_t *p_pmt )
992 msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d",
993 p_pmt->i_program_number, i_session_id );
995 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
996 0x3 /* only */, 0x1 /* ok_descrambling */,
1000 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1003 /*****************************************************************************
1005 *****************************************************************************/
1006 static void CAPMTAdd( access_t * p_access, int i_session_id,
1007 dvbpsi_pmt_t *p_pmt )
1012 msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
1013 p_pmt->i_program_number, i_session_id );
1015 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1016 0x4 /* add */, 0x1 /* ok_descrambling */,
1020 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1023 /*****************************************************************************
1025 *****************************************************************************/
1026 static void CAPMTUpdate( access_t * p_access, int i_session_id,
1027 dvbpsi_pmt_t *p_pmt )
1032 msg_Dbg( p_access, "updating CAPMT for SID %d on session %d",
1033 p_pmt->i_program_number, i_session_id );
1035 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1036 0x5 /* update */, 0x1 /* ok_descrambling */,
1040 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1043 /*****************************************************************************
1045 *****************************************************************************/
1046 static void CAPMTDelete( access_t * p_access, int i_session_id,
1047 dvbpsi_pmt_t *p_pmt )
1052 msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d",
1053 p_pmt->i_program_number, i_session_id );
1055 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1056 0x5 /* update */, 0x4 /* not selected */,
1060 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1063 /*****************************************************************************
1064 * ConditionalAccessHandle
1065 *****************************************************************************/
1066 static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
1067 uint8_t *p_apdu, int i_size )
1069 access_sys_t *p_sys = p_access->p_sys;
1070 system_ids_t *p_ids =
1071 (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1072 int i_tag = APDUGetTag( p_apdu, i_size );
1078 vlc_bool_t b_inited = VLC_FALSE;
1081 uint8_t *d = APDUGetLength( p_apdu, &l );
1082 msg_Dbg( p_access, "CA system IDs supported by the application :" );
1084 for ( i = 0; i < l / 2; i++ )
1086 p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
1088 msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] );
1090 p_ids->pi_system_ids[i] = 0;
1092 for ( i = 0; i < MAX_PROGRAMS; i++ )
1094 if ( p_sys->pp_selected_programs[i] != NULL )
1097 CAPMTAdd( p_access, i_session_id,
1098 p_sys->pp_selected_programs[i] );
1100 CAPMTFirst( p_access, i_session_id,
1101 p_sys->pp_selected_programs[i] );
1102 b_inited = VLC_TRUE;
1110 "unexpected tag in ConditionalAccessHandle (0x%x)",
1115 /*****************************************************************************
1116 * ConditionalAccessOpen
1117 *****************************************************************************/
1118 static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
1120 access_sys_t *p_sys = p_access->p_sys;
1122 msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
1124 p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
1125 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(system_ids_t));
1126 memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0,
1127 sizeof(system_ids_t) );
1129 APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
1142 /*****************************************************************************
1144 *****************************************************************************/
1145 static void DateTimeSend( access_t * p_access, int i_session_id )
1147 access_sys_t *p_sys = p_access->p_sys;
1148 date_time_t *p_date =
1149 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1151 time_t t = time(NULL);
1155 if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
1157 int Y = tm_gmt.tm_year;
1158 int M = tm_gmt.tm_mon + 1;
1159 int D = tm_gmt.tm_mday;
1160 int L = (M == 1 || M == 2) ? 1 : 0;
1161 int MJD = 14956 + D + (int)((Y - L) * 365.25)
1162 + (int)((M + 1 + L * 12) * 30.6001);
1163 uint8_t p_response[7];
1165 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
1167 p_response[0] = htons(MJD) >> 8;
1168 p_response[1] = htons(MJD) & 0xff;
1169 p_response[2] = DEC2BCD(tm_gmt.tm_hour);
1170 p_response[3] = DEC2BCD(tm_gmt.tm_min);
1171 p_response[4] = DEC2BCD(tm_gmt.tm_sec);
1172 p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
1173 p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
1175 APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
1177 p_date->i_last = mdate();
1181 /*****************************************************************************
1183 *****************************************************************************/
1184 static void DateTimeHandle( access_t * p_access, int i_session_id,
1185 uint8_t *p_apdu, int i_size )
1187 access_sys_t *p_sys = p_access->p_sys;
1188 date_time_t *p_date =
1189 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1191 int i_tag = APDUGetTag( p_apdu, i_size );
1195 case AOT_DATE_TIME_ENQ:
1198 const uint8_t *d = APDUGetLength( p_apdu, &l );
1202 p_date->i_interval = *d;
1203 msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
1204 p_date->i_interval );
1207 p_date->i_interval = 0;
1209 DateTimeSend( p_access, i_session_id );
1213 msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
1217 /*****************************************************************************
1219 *****************************************************************************/
1220 static void DateTimeManage( access_t * p_access, int i_session_id )
1222 access_sys_t *p_sys = p_access->p_sys;
1223 date_time_t *p_date =
1224 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1226 if ( p_date->i_interval
1227 && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
1229 DateTimeSend( p_access, i_session_id );
1233 /*****************************************************************************
1235 *****************************************************************************/
1236 static void DateTimeOpen( access_t * p_access, int i_session_id )
1238 access_sys_t *p_sys = p_access->p_sys;
1240 msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
1242 p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
1243 p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
1244 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t));
1245 memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
1247 DateTimeSend( p_access, i_session_id );
1254 /*****************************************************************************
1256 *****************************************************************************/
1257 static void MMIHandle( access_t * p_access, int i_session_id,
1258 uint8_t *p_apdu, int i_size )
1260 int i_tag = APDUGetTag( p_apdu, i_size );
1265 msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
1269 /*****************************************************************************
1271 *****************************************************************************/
1272 static void MMIOpen( access_t * p_access, int i_session_id )
1274 access_sys_t *p_sys = p_access->p_sys;
1276 msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
1278 p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
1286 /*****************************************************************************
1287 * InitSlot: Open the transport layer
1288 *****************************************************************************/
1289 #define MAX_TC_RETRIES 20
1291 static int InitSlot( access_t * p_access, int i_slot )
1293 access_sys_t *p_sys = p_access->p_sys;
1296 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1299 msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
1301 return VLC_EGENERIC;
1304 /* This is out of the spec */
1305 for ( i = 0; i < MAX_TC_RETRIES; i++ )
1308 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
1309 && i_tag == T_CTC_REPLY )
1311 p_sys->pb_active_slot[i_slot] = VLC_TRUE;
1315 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1319 "en50221_Init: couldn't send TPDU on slot %d",
1324 if ( p_sys->pb_active_slot[i_slot] )
1326 p_sys->i_ca_timeout = 100000;
1330 return VLC_EGENERIC;
1335 * External entry points
1338 /*****************************************************************************
1339 * en50221_Poll : Poll the CAM for TPDUs
1340 *****************************************************************************/
1341 int E_(en50221_Poll)( access_t * p_access )
1343 access_sys_t *p_sys = p_access->p_sys;
1347 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1351 if ( !p_sys->pb_active_slot[i_slot] )
1353 ca_slot_info_t sinfo;
1355 if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
1357 msg_Err( p_access, "en50221_Poll: couldn't get info on slot %d",
1362 if ( sinfo.flags & CA_CI_MODULE_READY )
1364 msg_Dbg( p_access, "en50221_Poll: slot %d is active",
1366 p_sys->pb_active_slot[i_slot] = VLC_TRUE;
1371 InitSlot( p_access, i_slot );
1374 if ( !p_sys->pb_tc_has_data[i_slot] )
1376 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, NULL, 0 ) !=
1380 "en50221_Poll: couldn't send TPDU on slot %d",
1384 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) !=
1388 "en50221_Poll: couldn't recv TPDU on slot %d",
1394 while ( p_sys->pb_tc_has_data[i_slot] )
1396 uint8_t p_tpdu[MAX_TPDU_SIZE];
1397 int i_size, i_session_size;
1400 if ( TPDUSend( p_access, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
1403 "en50221_Poll: couldn't send TPDU on slot %d",
1407 if ( TPDURecv( p_access, i_slot, &i_tag, p_tpdu, &i_size ) !=
1411 "en50221_Poll: couldn't recv TPDU on slot %d",
1416 p_session = GetLength( &p_tpdu[3], &i_session_size );
1417 if ( i_session_size <= 1 )
1423 if ( i_tag != T_DATA_LAST )
1426 "en50221_Poll: fragmented TPDU not supported" );
1430 SPDUHandle( p_access, i_slot, p_session, i_session_size );
1434 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
1436 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1437 && p_sys->p_sessions[i_session_id - 1].pf_manage )
1439 p_sys->p_sessions[i_session_id - 1].pf_manage( p_access,
1448 /*****************************************************************************
1449 * en50221_SetCAPMT :
1450 *****************************************************************************/
1451 int E_(en50221_SetCAPMT)( access_t * p_access, dvbpsi_pmt_t *p_pmt )
1453 access_sys_t *p_sys = p_access->p_sys;
1454 int i, i_session_id;
1455 vlc_bool_t b_update = VLC_FALSE;
1456 vlc_bool_t b_needs_descrambling = CAPMTNeedsDescrambling( p_pmt );
1458 for ( i = 0; i < MAX_PROGRAMS; i++ )
1460 if ( p_sys->pp_selected_programs[i] != NULL
1461 && p_sys->pp_selected_programs[i]->i_program_number
1462 == p_pmt->i_program_number )
1464 b_update = VLC_TRUE;
1466 if ( !b_needs_descrambling )
1468 dvbpsi_DeletePMT( p_pmt );
1469 p_pmt = p_sys->pp_selected_programs[i];
1470 p_sys->pp_selected_programs[i] = NULL;
1474 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
1475 p_sys->pp_selected_programs[i] = p_pmt;
1482 if ( !b_update && b_needs_descrambling )
1484 for ( i = 0; i < MAX_PROGRAMS; i++ )
1486 if ( p_sys->pp_selected_programs[i] == NULL )
1488 p_sys->pp_selected_programs[i] = p_pmt;
1494 if ( b_update || b_needs_descrambling )
1496 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
1498 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1499 == RI_CONDITIONAL_ACCESS_SUPPORT )
1501 if ( b_update && b_needs_descrambling )
1502 CAPMTUpdate( p_access, i_session_id, p_pmt );
1503 else if ( b_update )
1504 CAPMTDelete( p_access, i_session_id, p_pmt );
1506 CAPMTAdd( p_access, i_session_id, p_pmt );
1511 if ( !b_needs_descrambling )
1513 dvbpsi_DeletePMT( p_pmt );
1519 /*****************************************************************************
1521 *****************************************************************************/
1522 void E_(en50221_End)( access_t * p_access )
1524 access_sys_t *p_sys = p_access->p_sys;
1527 for ( i = 0; i < MAX_PROGRAMS; i++ )
1529 if ( p_sys->pp_selected_programs[i] != NULL )
1531 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );