1 /*****************************************************************************
2 * en50221.c : implementation of the transport, session and applications
4 *****************************************************************************
5 * Copyright (C) 2004-2005 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 *****************************************************************************/
29 #include <vlc_common.h>
30 #include <vlc_access.h>
32 #include <sys/ioctl.h>
35 #include <sys/types.h>
40 #include <netinet/in.h>
42 /* DVB Card Drivers */
43 #include <linux/dvb/version.h>
44 #include <linux/dvb/dmx.h>
45 #include <linux/dvb/frontend.h>
46 #include <linux/dvb/ca.h>
48 /* Include dvbpsi headers */
49 #ifdef HAVE_DVBPSI_DR_H
50 # include <dvbpsi/dvbpsi.h>
51 # include <dvbpsi/descriptor.h>
52 # include <dvbpsi/pat.h>
53 # include <dvbpsi/pmt.h>
54 # include <dvbpsi/dr.h>
55 # include <dvbpsi/psi.h>
56 # include <dvbpsi/demux.h>
57 # include <dvbpsi/sdt.h>
60 # include "descriptor.h"
61 # include "tables/pat.h"
62 # include "tables/pmt.h"
63 # include "descriptors/dr.h"
66 # include "tables/sdt.h"
70 # include <vlc_httpd.h>
75 #include <vlc_charset.h>
78 #define HLCI_WAIT_CAM_READY 0
79 #define CAM_PROG_MAX MAX_PROGRAMS
80 //#define CAPMT_WAIT 100 /* uncomment this for slow CAMs */
82 static void ResourceManagerOpen( access_t * p_access, int i_session_id );
83 static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
84 static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
85 static void DateTimeOpen( access_t * p_access, int i_session_id );
86 static void MMIOpen( access_t * p_access, int i_session_id );
88 /*****************************************************************************
90 *****************************************************************************/
91 #define SIZE_INDICATOR 0x80
93 static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
95 *pi_length = *p_data++;
97 if ( (*pi_length & SIZE_INDICATOR) != 0 )
99 int l = *pi_length & ~SIZE_INDICATOR;
103 for ( i = 0; i < l; i++ )
104 *pi_length = (*pi_length << 8) | *p_data++;
110 static uint8_t *SetLength( uint8_t *p_data, int i_length )
114 if ( i_length < 128 )
118 else if ( i_length < 256 )
120 *p++ = SIZE_INDICATOR | 0x1;
123 else if ( i_length < 65536 )
125 *p++ = SIZE_INDICATOR | 0x2;
126 *p++ = i_length >> 8;
127 *p++ = i_length & 0xff;
129 else if ( i_length < 16777216 )
131 *p++ = SIZE_INDICATOR | 0x3;
132 *p++ = i_length >> 16;
133 *p++ = (i_length >> 8) & 0xff;
134 *p++ = i_length & 0xff;
138 *p++ = SIZE_INDICATOR | 0x4;
139 *p++ = i_length >> 24;
140 *p++ = (i_length >> 16) & 0xff;
141 *p++ = (i_length >> 8) & 0xff;
142 *p++ = i_length & 0xff;
153 #define MAX_TPDU_SIZE 4096
154 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
156 #define DATA_INDICATOR 0x80
160 #define T_CREATE_TC 0x82
161 #define T_CTC_REPLY 0x83
162 #define T_DELETE_TC 0x84
163 #define T_DTC_REPLY 0x85
164 #define T_REQUEST_TC 0x86
165 #define T_NEW_TC 0x87
166 #define T_TC_ERROR 0x88
167 #define T_DATA_LAST 0xA0
168 #define T_DATA_MORE 0xA1
170 static void Dump( bool b_outgoing, uint8_t *p_data, int i_size )
175 fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
176 for ( i = 0; i < i_size && i < MAX_DUMP; i++)
177 fprintf(stderr, "%02X ", p_data[i]);
178 fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
180 VLC_UNUSED(b_outgoing); VLC_UNUSED(p_data); VLC_UNUSED(i_size);
184 /*****************************************************************************
186 *****************************************************************************/
187 static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
188 const uint8_t *p_content, int i_length )
190 access_sys_t *p_sys = p_access->p_sys;
191 uint8_t i_tcid = i_slot + 1;
192 uint8_t p_data[MAX_TPDU_SIZE];
208 p_data[3] = 1; /* length */
215 p_data[3] = 2; /* length */
217 p_data[5] = p_content[0];
224 /* i_length <= MAX_TPDU_DATA */
225 uint8_t *p = p_data + 3;
226 p = SetLength( p, i_length + 1 );
230 memcpy( p, p_content, i_length );
231 i_size = i_length + (p - p_data);
238 Dump( true, p_data, i_size );
240 if ( write( p_sys->i_ca_handle, p_data, i_size ) != i_size )
242 msg_Err( p_access, "cannot write to CAM device (%m)" );
250 /*****************************************************************************
252 *****************************************************************************/
253 #define CAM_READ_TIMEOUT 3500 // ms
255 static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
256 uint8_t *p_data, int *pi_size )
258 access_sys_t *p_sys = p_access->p_sys;
259 uint8_t i_tcid = i_slot + 1;
261 struct pollfd pfd[1];
263 pfd[0].fd = p_sys->i_ca_handle;
264 pfd[0].events = POLLIN;
265 if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
267 msg_Err( p_access, "cannot poll from CAM device" );
271 if ( pi_size == NULL )
273 p_data = xmalloc( MAX_TPDU_SIZE );
278 i_size = read( p_sys->i_ca_handle, p_data, MAX_TPDU_SIZE );
280 if ( i_size >= 0 || errno != EINTR )
286 msg_Err( p_access, "cannot read from CAM device (%d:%m)", i_size );
287 if( pi_size == NULL )
292 if ( p_data[1] != i_tcid )
294 msg_Err( p_access, "invalid read from CAM device (%d instead of %d)",
296 if( pi_size == NULL )
302 p_sys->pb_tc_has_data[i_slot] = (i_size >= 4
303 && p_data[i_size - 4] == T_SB
304 && p_data[i_size - 3] == 2
305 && (p_data[i_size - 1] & DATA_INDICATOR))
308 Dump( false, p_data, i_size );
310 if ( pi_size == NULL )
323 #define ST_SESSION_NUMBER 0x90
324 #define ST_OPEN_SESSION_REQUEST 0x91
325 #define ST_OPEN_SESSION_RESPONSE 0x92
326 #define ST_CREATE_SESSION 0x93
327 #define ST_CREATE_SESSION_RESPONSE 0x94
328 #define ST_CLOSE_SESSION_REQUEST 0x95
329 #define ST_CLOSE_SESSION_RESPONSE 0x96
332 #define SS_NOT_ALLOCATED 0xF0
334 #define RI_RESOURCE_MANAGER 0x00010041
335 #define RI_APPLICATION_INFORMATION 0x00020041
336 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
337 #define RI_HOST_CONTROL 0x00200041
338 #define RI_DATE_TIME 0x00240041
339 #define RI_MMI 0x00400041
341 static int ResourceIdToInt( uint8_t *p_data )
343 return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
344 | ((int)p_data[2] << 8) | p_data[3];
347 /*****************************************************************************
349 *****************************************************************************/
350 static int SPDUSend( access_t * p_access, int i_session_id,
351 uint8_t *p_data, int i_size )
353 access_sys_t *p_sys = p_access->p_sys;
354 uint8_t *p_spdu = xmalloc( i_size + 4 );
357 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
359 *p++ = ST_SESSION_NUMBER;
361 *p++ = (i_session_id >> 8);
362 *p++ = i_session_id & 0xff;
364 memcpy( p, p_data, i_size );
371 if ( i_size > MAX_TPDU_DATA )
373 if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
374 MAX_TPDU_DATA ) != VLC_SUCCESS )
376 msg_Err( p_access, "couldn't send TPDU on session %d",
382 i_size -= MAX_TPDU_DATA;
386 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
389 msg_Err( p_access, "couldn't send TPDU on session %d",
397 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
400 msg_Err( p_access, "couldn't recv TPDU on session %d",
411 /*****************************************************************************
413 *****************************************************************************/
414 static void SessionOpen( access_t * p_access, uint8_t i_slot,
415 uint8_t *p_spdu, int i_size )
417 VLC_UNUSED( i_size );
419 access_sys_t *p_sys = p_access->p_sys;
421 int i_resource_id = ResourceIdToInt( &p_spdu[2] );
422 uint8_t p_response[16];
423 int i_status = SS_NOT_ALLOCATED;
426 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
428 if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
431 if ( i_session_id > MAX_SESSIONS )
433 msg_Err( p_access, "too many sessions !" );
436 p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
437 p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
438 p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
439 p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
441 if ( i_resource_id == RI_RESOURCE_MANAGER
442 || i_resource_id == RI_APPLICATION_INFORMATION
443 || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
444 || i_resource_id == RI_DATE_TIME
445 || i_resource_id == RI_MMI )
450 p_response[0] = ST_OPEN_SESSION_RESPONSE;
452 p_response[2] = i_status;
453 p_response[3] = p_spdu[2];
454 p_response[4] = p_spdu[3];
455 p_response[5] = p_spdu[4];
456 p_response[6] = p_spdu[5];
457 p_response[7] = i_session_id >> 8;
458 p_response[8] = i_session_id & 0xff;
460 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) !=
464 "SessionOpen: couldn't send TPDU on slot %d", i_slot );
467 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
470 "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
474 switch ( i_resource_id )
476 case RI_RESOURCE_MANAGER:
477 ResourceManagerOpen( p_access, i_session_id ); break;
478 case RI_APPLICATION_INFORMATION:
479 ApplicationInformationOpen( p_access, i_session_id ); break;
480 case RI_CONDITIONAL_ACCESS_SUPPORT:
481 ConditionalAccessOpen( p_access, i_session_id ); break;
483 DateTimeOpen( p_access, i_session_id ); break;
485 MMIOpen( p_access, i_session_id ); break;
487 case RI_HOST_CONTROL:
489 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
490 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
495 /* unused code for the moment - commented out to keep gcc happy */
496 /*****************************************************************************
498 *****************************************************************************/
499 static void SessionCreate( access_t * p_access, int i_slot, int i_resource_id )
501 access_sys_t *p_sys = p_access->p_sys;
502 uint8_t p_response[16];
506 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
508 if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
511 if ( i_session_id == MAX_SESSIONS )
513 msg_Err( p_access, "too many sessions !" );
516 p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
517 p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
518 p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
519 p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
520 p_sys->p_sessions[i_session_id - 1].p_sys = NULL;
522 p_response[0] = ST_CREATE_SESSION;
524 p_response[2] = i_resource_id >> 24;
525 p_response[3] = (i_resource_id >> 16) & 0xff;
526 p_response[4] = (i_resource_id >> 8) & 0xff;
527 p_response[5] = i_resource_id & 0xff;
528 p_response[6] = i_session_id >> 8;
529 p_response[7] = i_session_id & 0xff;
531 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
535 "SessionCreate: couldn't send TPDU on slot %d", i_slot );
538 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
541 "SessionCreate: couldn't recv TPDU on slot %d", i_slot );
547 /*****************************************************************************
548 * SessionCreateResponse
549 *****************************************************************************/
550 static void SessionCreateResponse( access_t * p_access, uint8_t i_slot,
551 uint8_t *p_spdu, int i_size )
553 VLC_UNUSED( i_size );
554 VLC_UNUSED( i_slot );
556 access_sys_t *p_sys = p_access->p_sys;
557 int i_status = p_spdu[2];
558 int i_resource_id = ResourceIdToInt( &p_spdu[3] );
559 int i_session_id = ((int)p_spdu[7] << 8) | p_spdu[8];
561 if ( i_status != SS_OK )
563 msg_Err( p_access, "SessionCreateResponse: failed to open session %d"
564 " resource=0x%x status=0x%x", i_session_id, i_resource_id,
566 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
570 switch ( i_resource_id )
572 case RI_RESOURCE_MANAGER:
573 ResourceManagerOpen( p_access, i_session_id ); break;
574 case RI_APPLICATION_INFORMATION:
575 ApplicationInformationOpen( p_access, i_session_id ); break;
576 case RI_CONDITIONAL_ACCESS_SUPPORT:
577 ConditionalAccessOpen( p_access, i_session_id ); break;
579 DateTimeOpen( p_access, i_session_id ); break;
581 MMIOpen( p_access, i_session_id ); break;
583 case RI_HOST_CONTROL:
585 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
586 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
590 /*****************************************************************************
592 *****************************************************************************/
593 static void SessionSendClose( access_t * p_access, int i_session_id )
595 access_sys_t *p_sys = p_access->p_sys;
596 uint8_t p_response[16];
598 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
600 p_response[0] = ST_CLOSE_SESSION_REQUEST;
602 p_response[2] = i_session_id >> 8;
603 p_response[3] = i_session_id & 0xff;
605 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
609 "SessionSendClose: couldn't send TPDU on slot %d", i_slot );
612 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
615 "SessionSendClose: couldn't recv TPDU on slot %d", i_slot );
620 /*****************************************************************************
622 *****************************************************************************/
623 static void SessionClose( access_t * p_access, int i_session_id )
625 access_sys_t *p_sys = p_access->p_sys;
626 uint8_t p_response[16];
628 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
630 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
631 p_sys->p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
632 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
634 p_response[0] = ST_CLOSE_SESSION_RESPONSE;
636 p_response[2] = SS_OK;
637 p_response[3] = i_session_id >> 8;
638 p_response[4] = i_session_id & 0xff;
640 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
644 "SessionClose: couldn't send TPDU on slot %d", i_slot );
647 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
650 "SessionClose: couldn't recv TPDU on slot %d", i_slot );
655 /*****************************************************************************
657 *****************************************************************************/
658 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
659 uint8_t *p_spdu, int i_size )
661 access_sys_t *p_sys = p_access->p_sys;
666 case ST_SESSION_NUMBER:
669 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
670 p_sys->p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
671 p_spdu + 4, i_size - 4 );
674 case ST_OPEN_SESSION_REQUEST:
675 if ( i_size != 6 || p_spdu[1] != 0x4 )
677 SessionOpen( p_access, i_slot, p_spdu, i_size );
680 case ST_CREATE_SESSION_RESPONSE:
681 if ( i_size != 9 || p_spdu[1] != 0x7 )
683 SessionCreateResponse( p_access, i_slot, p_spdu, i_size );
686 case ST_CLOSE_SESSION_REQUEST:
687 if ( i_size != 4 || p_spdu[1] != 0x2 )
689 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
690 SessionClose( p_access, i_session_id );
693 case ST_CLOSE_SESSION_RESPONSE:
694 if ( i_size != 5 || p_spdu[1] != 0x3 )
696 i_session_id = ((int)p_spdu[3] << 8) | p_spdu[4];
699 msg_Err( p_access, "closing a session which is not allocated (%d)",
704 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
705 p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
707 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
712 msg_Err( p_access, "unexpected tag in SPDUHandle (%x)", p_spdu[0] );
722 #define AOT_NONE 0x000000
723 #define AOT_PROFILE_ENQ 0x9F8010
724 #define AOT_PROFILE 0x9F8011
725 #define AOT_PROFILE_CHANGE 0x9F8012
726 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
727 #define AOT_APPLICATION_INFO 0x9F8021
728 #define AOT_ENTER_MENU 0x9F8022
729 #define AOT_CA_INFO_ENQ 0x9F8030
730 #define AOT_CA_INFO 0x9F8031
731 #define AOT_CA_PMT 0x9F8032
732 #define AOT_CA_PMT_REPLY 0x9F8033
733 #define AOT_TUNE 0x9F8400
734 #define AOT_REPLACE 0x9F8401
735 #define AOT_CLEAR_REPLACE 0x9F8402
736 #define AOT_ASK_RELEASE 0x9F8403
737 #define AOT_DATE_TIME_ENQ 0x9F8440
738 #define AOT_DATE_TIME 0x9F8441
739 #define AOT_CLOSE_MMI 0x9F8800
740 #define AOT_DISPLAY_CONTROL 0x9F8801
741 #define AOT_DISPLAY_REPLY 0x9F8802
742 #define AOT_TEXT_LAST 0x9F8803
743 #define AOT_TEXT_MORE 0x9F8804
744 #define AOT_KEYPAD_CONTROL 0x9F8805
745 #define AOT_KEYPRESS 0x9F8806
746 #define AOT_ENQ 0x9F8807
747 #define AOT_ANSW 0x9F8808
748 #define AOT_MENU_LAST 0x9F8809
749 #define AOT_MENU_MORE 0x9F880A
750 #define AOT_MENU_ANSW 0x9F880B
751 #define AOT_LIST_LAST 0x9F880C
752 #define AOT_LIST_MORE 0x9F880D
753 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
754 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
755 #define AOT_DISPLAY_MESSAGE 0x9F8810
756 #define AOT_SCENE_END_MARK 0x9F8811
757 #define AOT_SCENE_DONE 0x9F8812
758 #define AOT_SCENE_CONTROL 0x9F8813
759 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
760 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
761 #define AOT_FLUSH_DOWNLOAD 0x9F8816
762 #define AOT_DOWNLOAD_REPLY 0x9F8817
763 #define AOT_COMMS_CMD 0x9F8C00
764 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
765 #define AOT_COMMS_REPLY 0x9F8C02
766 #define AOT_COMMS_SEND_LAST 0x9F8C03
767 #define AOT_COMMS_SEND_MORE 0x9F8C04
768 #define AOT_COMMS_RCV_LAST 0x9F8C05
769 #define AOT_COMMS_RCV_MORE 0x9F8C06
771 /*****************************************************************************
773 *****************************************************************************/
774 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
779 for ( i = 0; i < 3; i++ )
780 t = (t << 8) | *p_apdu++;
787 /*****************************************************************************
789 *****************************************************************************/
790 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
792 return GetLength( &p_apdu[3], pi_size );
795 /*****************************************************************************
797 *****************************************************************************/
798 static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
799 uint8_t *p_data, int i_size )
801 access_sys_t *p_sys = p_access->p_sys;
802 uint8_t *p_apdu = xmalloc( i_size + 12 );
807 *p++ = (i_tag >> 16);
808 *p++ = (i_tag >> 8) & 0xff;
810 p = SetLength( p, i_size );
812 memcpy( p, p_data, i_size );
813 if ( p_sys->i_ca_type == CA_CI_LINK )
815 i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
819 if ( i_size + p - p_apdu > 256 )
821 msg_Err( p_access, "CAM: apdu overflow" );
822 i_ret = VLC_EGENERIC;
826 ca_msg.length = i_size + p - p_apdu;
827 if ( i_size == 0 ) ca_msg.length=3;
828 memcpy( ca_msg.msg, p_apdu, i_size + p - p_apdu );
829 i_ret = ioctl(p_sys->i_ca_handle, CA_SEND_MSG, &ca_msg );
832 msg_Err( p_access, "Error sending to CAM: %m" );
833 i_ret = VLC_EGENERIC;
845 /*****************************************************************************
846 * ResourceManagerHandle
847 *****************************************************************************/
848 static void ResourceManagerHandle( access_t * p_access, int i_session_id,
849 uint8_t *p_apdu, int i_size )
851 int i_tag = APDUGetTag( p_apdu, i_size );
855 case AOT_PROFILE_ENQ:
857 int resources[] = { htonl(RI_RESOURCE_MANAGER),
858 htonl(RI_APPLICATION_INFORMATION),
859 htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
863 APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
868 APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
872 msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
877 /*****************************************************************************
878 * ResourceManagerOpen
879 *****************************************************************************/
880 static void ResourceManagerOpen( access_t * p_access, int i_session_id )
882 access_sys_t *p_sys = p_access->p_sys;
884 msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
886 p_sys->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
888 APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
892 * Application Information
895 /*****************************************************************************
896 * ApplicationInformationEnterMenu
897 *****************************************************************************/
898 static void ApplicationInformationEnterMenu( access_t * p_access,
901 access_sys_t *p_sys = p_access->p_sys;
902 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
904 msg_Dbg( p_access, "entering MMI menus on session %d", i_session_id );
905 APDUSend( p_access, i_session_id, AOT_ENTER_MENU, NULL, 0 );
906 p_sys->pb_slot_mmi_expected[i_slot] = true;
909 /*****************************************************************************
910 * ApplicationInformationHandle
911 *****************************************************************************/
912 static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
913 uint8_t *p_apdu, int i_size )
915 VLC_UNUSED(i_session_id);
916 int i_tag = APDUGetTag( p_apdu, i_size );
920 case AOT_APPLICATION_INFO:
922 int i_type, i_manufacturer, i_code;
924 uint8_t *d = APDUGetLength( p_apdu, &l );
927 p_apdu[l + 4] = '\0';
930 i_manufacturer = ((int)d[0] << 8) | d[1];
932 i_code = ((int)d[0] << 8) | d[1];
934 d = GetLength( d, &l );
936 msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
937 d, i_type, i_manufacturer, i_code );
942 "unexpected tag in ApplicationInformationHandle (0x%x)",
947 /*****************************************************************************
948 * ApplicationInformationOpen
949 *****************************************************************************/
950 static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
952 access_sys_t *p_sys = p_access->p_sys;
954 msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
956 p_sys->p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
958 APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
965 #define MAX_CASYSTEM_IDS 64
969 uint16_t pi_system_ids[MAX_CASYSTEM_IDS + 1];
972 static bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
975 if( !p_ids ) return true; /* dummy session for high-level CI intf */
977 while ( p_ids->pi_system_ids[i] )
979 if ( p_ids->pi_system_ids[i] == i_id )
987 /*****************************************************************************
988 * CAPMTNeedsDescrambling
989 *****************************************************************************/
990 static bool CAPMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
992 dvbpsi_descriptor_t *p_dr;
993 dvbpsi_pmt_es_t *p_es;
995 for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
997 if( p_dr->i_tag == 0x9 )
1003 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1005 for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
1006 p_dr = p_dr->p_next )
1008 if( p_dr->i_tag == 0x9 )
1018 /*****************************************************************************
1020 *****************************************************************************/
1021 static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr )
1025 while ( p_dr != NULL )
1027 if( p_dr->i_tag == 0x9 )
1029 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1031 if ( CheckSystemID( p_ids, i_sysid ) )
1032 i_cad_size += p_dr->i_length + 2;
1034 p_dr = p_dr->p_next;
1040 static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt,
1041 uint16_t i_program_number, uint8_t i_version,
1042 int i_size, dvbpsi_descriptor_t *p_dr,
1048 p_data = xmalloc( 7 + i_size );
1050 p_data = xmalloc( 6 );
1052 p_data[0] = i_list_mgt;
1053 p_data[1] = i_program_number >> 8;
1054 p_data[2] = i_program_number & 0xff;
1055 p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
1061 p_data[4] = (i_size + 1) >> 8;
1062 p_data[5] = (i_size + 1) & 0xff;
1066 while ( p_dr != NULL )
1068 if( p_dr->i_tag == 0x9 )
1070 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1072 if ( CheckSystemID( p_ids, i_sysid ) )
1075 p_data[i + 1] = p_dr->i_length;
1076 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1077 // p_data[i+4] &= 0x1f;
1078 i += p_dr->i_length + 2;
1081 p_dr = p_dr->p_next;
1093 static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt,
1094 int i_capmt_size, uint8_t i_type, uint16_t i_pid,
1095 int i_size, dvbpsi_descriptor_t *p_dr,
1102 p_data = xrealloc( p_capmt, i_capmt_size + 6 + i_size );
1104 p_data = xrealloc( p_capmt, i_capmt_size + 5 );
1109 p_data[i + 1] = i_pid >> 8;
1110 p_data[i + 2] = i_pid & 0xff;
1114 p_data[i + 3] = (i_size + 1) >> 8;
1115 p_data[i + 4] = (i_size + 1) & 0xff;
1116 p_data[i + 5] = i_cmd;
1119 while ( p_dr != NULL )
1121 if( p_dr->i_tag == 0x9 )
1123 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1125 if ( CheckSystemID( p_ids, i_sysid ) )
1128 p_data[i + 1] = p_dr->i_length;
1129 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1130 i += p_dr->i_length + 2;
1133 p_dr = p_dr->p_next;
1145 static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id,
1146 dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt,
1147 uint8_t i_cmd, int *pi_capmt_size )
1149 access_sys_t *p_sys = p_access->p_sys;
1150 system_ids_t *p_ids =
1151 (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1152 dvbpsi_pmt_es_t *p_es;
1153 int i_cad_size, i_cad_program_size;
1156 i_cad_size = i_cad_program_size =
1157 GetCADSize( p_ids, p_pmt->p_first_descriptor );
1158 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1160 i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor );
1166 "no compatible scrambling system for SID %d on session %d",
1167 p_pmt->i_program_number, i_session_id );
1172 p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number,
1173 p_pmt->i_version, i_cad_program_size,
1174 p_pmt->p_first_descriptor, i_cmd );
1176 if ( i_cad_program_size )
1177 *pi_capmt_size = 7 + i_cad_program_size;
1181 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1183 i_cad_size = GetCADSize( p_ids, p_es->p_first_descriptor );
1185 if ( i_cad_size || i_cad_program_size )
1187 p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type,
1188 p_es->i_pid, i_cad_size,
1189 p_es->p_first_descriptor, i_cmd );
1191 *pi_capmt_size += 6 + i_cad_size;
1193 *pi_capmt_size += 5;
1200 /*****************************************************************************
1202 *****************************************************************************/
1203 static void CAPMTFirst( access_t * p_access, int i_session_id,
1204 dvbpsi_pmt_t *p_pmt )
1209 msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d",
1210 p_pmt->i_program_number, i_session_id );
1212 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1213 0x3 /* only */, 0x1 /* ok_descrambling */,
1218 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1223 /*****************************************************************************
1225 *****************************************************************************/
1226 static void CAPMTAdd( access_t * p_access, int i_session_id,
1227 dvbpsi_pmt_t *p_pmt )
1232 if( p_access->p_sys->i_selected_programs >= CAM_PROG_MAX )
1234 msg_Warn( p_access, "Not adding CAPMT for SID %d, too many programs",
1235 p_pmt->i_program_number );
1238 p_access->p_sys->i_selected_programs++;
1239 if( p_access->p_sys->i_selected_programs == 1 )
1241 CAPMTFirst( p_access, i_session_id, p_pmt );
1246 msleep( CAPMT_WAIT * 1000 );
1249 msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
1250 p_pmt->i_program_number, i_session_id );
1252 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1253 0x4 /* add */, 0x1 /* ok_descrambling */,
1258 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1263 /*****************************************************************************
1265 *****************************************************************************/
1266 static void CAPMTUpdate( access_t * p_access, int i_session_id,
1267 dvbpsi_pmt_t *p_pmt )
1272 msg_Dbg( p_access, "updating CAPMT for SID %d on session %d",
1273 p_pmt->i_program_number, i_session_id );
1275 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1276 0x5 /* update */, 0x1 /* ok_descrambling */,
1281 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1286 /*****************************************************************************
1288 *****************************************************************************/
1289 static void CAPMTDelete( access_t * p_access, int i_session_id,
1290 dvbpsi_pmt_t *p_pmt )
1295 p_access->p_sys->i_selected_programs--;
1296 msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d",
1297 p_pmt->i_program_number, i_session_id );
1299 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1300 0x5 /* update */, 0x4 /* not selected */,
1305 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1310 /*****************************************************************************
1311 * ConditionalAccessHandle
1312 *****************************************************************************/
1313 static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
1314 uint8_t *p_apdu, int i_size )
1316 access_sys_t *p_sys = p_access->p_sys;
1317 system_ids_t *p_ids =
1318 (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1319 int i_tag = APDUGetTag( p_apdu, i_size );
1327 uint8_t *d = APDUGetLength( p_apdu, &l );
1328 msg_Dbg( p_access, "CA system IDs supported by the application :" );
1330 for ( i = 0; i < l / 2; i++ )
1332 p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
1334 msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] );
1336 p_ids->pi_system_ids[i] = 0;
1338 for ( i = 0; i < MAX_PROGRAMS; i++ )
1340 if ( p_sys->pp_selected_programs[i] != NULL )
1342 CAPMTAdd( p_access, i_session_id,
1343 p_sys->pp_selected_programs[i] );
1351 "unexpected tag in ConditionalAccessHandle (0x%x)",
1356 /*****************************************************************************
1357 * ConditionalAccessClose
1358 *****************************************************************************/
1359 static void ConditionalAccessClose( access_t * p_access, int i_session_id )
1361 access_sys_t *p_sys = p_access->p_sys;
1363 msg_Dbg( p_access, "closing ConditionalAccess session (%d)", i_session_id );
1365 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1368 /*****************************************************************************
1369 * ConditionalAccessOpen
1370 *****************************************************************************/
1371 static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
1373 access_sys_t *p_sys = p_access->p_sys;
1375 msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
1377 p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
1378 p_sys->p_sessions[i_session_id - 1].pf_close = ConditionalAccessClose;
1379 p_sys->p_sessions[i_session_id - 1].p_sys = calloc( 1, sizeof(system_ids_t) );
1381 APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
1394 /*****************************************************************************
1396 *****************************************************************************/
1397 static void DateTimeSend( access_t * p_access, int i_session_id )
1399 access_sys_t *p_sys = p_access->p_sys;
1400 date_time_t *p_date =
1401 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1403 time_t t = time(NULL);
1407 if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
1409 int Y = tm_gmt.tm_year;
1410 int M = tm_gmt.tm_mon + 1;
1411 int D = tm_gmt.tm_mday;
1412 int L = (M == 1 || M == 2) ? 1 : 0;
1413 int MJD = 14956 + D + (int)((Y - L) * 365.25)
1414 + (int)((M + 1 + L * 12) * 30.6001);
1415 uint8_t p_response[7];
1417 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
1419 p_response[0] = htons(MJD) >> 8;
1420 p_response[1] = htons(MJD) & 0xff;
1421 p_response[2] = DEC2BCD(tm_gmt.tm_hour);
1422 p_response[3] = DEC2BCD(tm_gmt.tm_min);
1423 p_response[4] = DEC2BCD(tm_gmt.tm_sec);
1424 p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
1425 p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
1427 APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
1429 p_date->i_last = mdate();
1433 /*****************************************************************************
1435 *****************************************************************************/
1436 static void DateTimeHandle( access_t * p_access, int i_session_id,
1437 uint8_t *p_apdu, int i_size )
1439 access_sys_t *p_sys = p_access->p_sys;
1440 date_time_t *p_date =
1441 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1443 int i_tag = APDUGetTag( p_apdu, i_size );
1447 case AOT_DATE_TIME_ENQ:
1450 const uint8_t *d = APDUGetLength( p_apdu, &l );
1454 p_date->i_interval = *d;
1455 msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
1456 p_date->i_interval );
1459 p_date->i_interval = 0;
1461 DateTimeSend( p_access, i_session_id );
1465 msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
1469 /*****************************************************************************
1471 *****************************************************************************/
1472 static void DateTimeManage( access_t * p_access, int i_session_id )
1474 access_sys_t *p_sys = p_access->p_sys;
1475 date_time_t *p_date =
1476 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1478 if ( p_date->i_interval
1479 && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
1481 DateTimeSend( p_access, i_session_id );
1485 /*****************************************************************************
1487 *****************************************************************************/
1488 static void DateTimeClose( access_t * p_access, int i_session_id )
1490 access_sys_t *p_sys = p_access->p_sys;
1492 msg_Dbg( p_access, "closing DateTime session (%d)", i_session_id );
1494 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1497 /*****************************************************************************
1499 *****************************************************************************/
1500 static void DateTimeOpen( access_t * p_access, int i_session_id )
1502 access_sys_t *p_sys = p_access->p_sys;
1504 msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
1506 p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
1507 p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
1508 p_sys->p_sessions[i_session_id - 1].pf_close = DateTimeClose;
1509 p_sys->p_sessions[i_session_id - 1].p_sys = calloc( 1, sizeof(date_time_t) );
1511 DateTimeSend( p_access, i_session_id );
1518 /* Display Control Commands */
1520 #define DCC_SET_MMI_MODE 0x01
1521 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
1522 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
1523 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
1524 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
1528 #define MM_HIGH_LEVEL 0x01
1529 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
1530 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
1532 /* Display Reply IDs */
1534 #define DRI_MMI_MODE_ACK 0x01
1535 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
1536 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
1537 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
1538 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
1539 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
1540 #define DRI_UNKNOWN_MMI_MODE 0xF1
1541 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
1545 #define EF_BLIND 0x01
1549 #define AI_CANCEL 0x00
1550 #define AI_ANSWER 0x01
1554 en50221_mmi_object_t last_object;
1557 /*****************************************************************************
1559 *****************************************************************************/
1560 static void MMISendObject( access_t *p_access, int i_session_id,
1561 en50221_mmi_object_t *p_object )
1563 access_sys_t *p_sys = p_access->p_sys;
1564 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1568 switch ( p_object->i_object_type )
1570 case EN50221_MMI_ANSW:
1572 i_size = 1 + strlen( p_object->u.answ.psz_answ );
1573 p_data = xmalloc( i_size );
1574 p_data[0] = (p_object->u.answ.b_ok == true) ? 0x1 : 0x0;
1575 strncpy( (char *)&p_data[1], p_object->u.answ.psz_answ, i_size - 1 );
1578 case EN50221_MMI_MENU_ANSW:
1579 i_tag = AOT_MENU_ANSW;
1581 p_data = xmalloc( i_size );
1582 p_data[0] = p_object->u.menu_answ.i_choice;
1586 msg_Err( p_access, "unknown MMI object %d", p_object->i_object_type );
1590 APDUSend( p_access, i_session_id, i_tag, p_data, i_size );
1593 p_sys->pb_slot_mmi_expected[i_slot] = true;
1596 /*****************************************************************************
1598 *****************************************************************************/
1599 static void MMISendClose( access_t *p_access, int i_session_id )
1601 access_sys_t *p_sys = p_access->p_sys;
1602 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1604 APDUSend( p_access, i_session_id, AOT_CLOSE_MMI, NULL, 0 );
1606 p_sys->pb_slot_mmi_expected[i_slot] = true;
1609 /*****************************************************************************
1611 *****************************************************************************/
1612 static void MMIDisplayReply( access_t *p_access, int i_session_id )
1614 uint8_t p_response[2];
1616 p_response[0] = DRI_MMI_MODE_ACK;
1617 p_response[1] = MM_HIGH_LEVEL;
1619 APDUSend( p_access, i_session_id, AOT_DISPLAY_REPLY, p_response, 2 );
1621 msg_Dbg( p_access, "sending DisplayReply on session (%d)", i_session_id );
1624 /*****************************************************************************
1626 *****************************************************************************/
1627 static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size )
1629 int i_tag = APDUGetTag( *pp_apdu, *pi_size );
1633 if ( i_tag != AOT_TEXT_LAST )
1635 msg_Err( p_access, "unexpected text tag: %06x", i_tag );
1637 return strdup( "" );
1640 d = APDUGetLength( *pp_apdu, &l );
1645 return dvbsi_to_utf8((char*)d,l);
1648 /*****************************************************************************
1650 *****************************************************************************/
1651 static void MMIHandleEnq( access_t *p_access, int i_session_id,
1652 uint8_t *p_apdu, int i_size )
1654 VLC_UNUSED( i_size );
1656 access_sys_t *p_sys = p_access->p_sys;
1657 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1658 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1660 uint8_t *d = APDUGetLength( p_apdu, &l );
1662 en50221_MMIFree( &p_mmi->last_object );
1663 p_mmi->last_object.i_object_type = EN50221_MMI_ENQ;
1664 p_mmi->last_object.u.enq.b_blind = (*d & 0x1) ? true : false;
1665 d += 2; /* skip answer_text_length because it is not mandatory */
1667 p_mmi->last_object.u.enq.psz_text = xmalloc( l + 1 );
1668 strncpy( p_mmi->last_object.u.enq.psz_text, (char *)d, l );
1669 p_mmi->last_object.u.enq.psz_text[l] = '\0';
1671 msg_Dbg( p_access, "MMI enq: %s%s", p_mmi->last_object.u.enq.psz_text,
1672 p_mmi->last_object.u.enq.b_blind == true ? " (blind)" : "" );
1673 p_sys->pb_slot_mmi_expected[i_slot] = false;
1674 p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
1677 /*****************************************************************************
1679 *****************************************************************************/
1680 static void MMIHandleMenu( access_t *p_access, int i_session_id, int i_tag,
1681 uint8_t *p_apdu, int i_size )
1684 access_sys_t *p_sys = p_access->p_sys;
1685 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1686 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1688 uint8_t *d = APDUGetLength( p_apdu, &l );
1690 en50221_MMIFree( &p_mmi->last_object );
1691 p_mmi->last_object.i_object_type = (i_tag == AOT_MENU_LAST) ?
1692 EN50221_MMI_MENU : EN50221_MMI_LIST;
1693 p_mmi->last_object.u.menu.i_choices = 0;
1694 p_mmi->last_object.u.menu.ppsz_choices = NULL;
1698 l--; d++; /* choice_nb */
1700 #define GET_FIELD( x ) \
1703 p_mmi->last_object.u.menu.psz_##x \
1704 = MMIGetText( p_access, &d, &l ); \
1705 msg_Dbg( p_access, "MMI " STRINGIFY( x ) ": %s", \
1706 p_mmi->last_object.u.menu.psz_##x ); \
1710 GET_FIELD( subtitle );
1711 GET_FIELD( bottom );
1716 char *psz_text = MMIGetText( p_access, &d, &l );
1717 TAB_APPEND( p_mmi->last_object.u.menu.i_choices,
1718 p_mmi->last_object.u.menu.ppsz_choices,
1720 msg_Dbg( p_access, "MMI choice: %s", psz_text );
1723 p_sys->pb_slot_mmi_expected[i_slot] = false;
1724 p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
1727 /*****************************************************************************
1729 *****************************************************************************/
1730 static void MMIHandle( access_t *p_access, int i_session_id,
1731 uint8_t *p_apdu, int i_size )
1733 int i_tag = APDUGetTag( p_apdu, i_size );
1737 case AOT_DISPLAY_CONTROL:
1740 uint8_t *d = APDUGetLength( p_apdu, &l );
1746 case DCC_SET_MMI_MODE:
1747 if ( l == 2 && d[1] == MM_HIGH_LEVEL )
1748 MMIDisplayReply( p_access, i_session_id );
1750 msg_Err( p_access, "unsupported MMI mode %02x", d[1] );
1754 msg_Err( p_access, "unsupported display control command %02x",
1763 MMIHandleEnq( p_access, i_session_id, p_apdu, i_size );
1768 MMIHandleMenu( p_access, i_session_id, i_tag, p_apdu, i_size );
1772 SessionSendClose( p_access, i_session_id );
1776 msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
1780 /*****************************************************************************
1782 *****************************************************************************/
1783 static void MMIClose( access_t *p_access, int i_session_id )
1785 access_sys_t *p_sys = p_access->p_sys;
1786 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1787 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1789 en50221_MMIFree( &p_mmi->last_object );
1790 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1792 msg_Dbg( p_access, "closing MMI session (%d)", i_session_id );
1793 p_sys->pb_slot_mmi_expected[i_slot] = false;
1794 p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
1797 /*****************************************************************************
1799 *****************************************************************************/
1800 static void MMIOpen( access_t *p_access, int i_session_id )
1802 access_sys_t *p_sys = p_access->p_sys;
1805 msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
1807 p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
1808 p_sys->p_sessions[i_session_id - 1].pf_close = MMIClose;
1809 p_sys->p_sessions[i_session_id - 1].p_sys = xmalloc(sizeof(mmi_t));
1810 p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1811 p_mmi->last_object.i_object_type = EN50221_MMI_NONE;
1819 /*****************************************************************************
1820 * InitSlot: Open the transport layer
1821 *****************************************************************************/
1822 #define MAX_TC_RETRIES 20
1824 static int InitSlot( access_t * p_access, int i_slot )
1826 access_sys_t *p_sys = p_access->p_sys;
1829 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1832 msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
1834 return VLC_EGENERIC;
1837 /* This is out of the spec */
1838 for ( i = 0; i < MAX_TC_RETRIES; i++ )
1841 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
1842 && i_tag == T_CTC_REPLY )
1844 p_sys->pb_active_slot[i_slot] = true;
1848 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1852 "en50221_Init: couldn't send TPDU on slot %d",
1858 if ( p_sys->pb_active_slot[i_slot] )
1860 p_sys->i_ca_timeout = 100000;
1864 return VLC_EGENERIC;
1869 * External entry points
1872 /*****************************************************************************
1873 * en50221_Init : Initialize the CAM for en50221
1874 *****************************************************************************/
1875 int en50221_Init( access_t * p_access )
1877 access_sys_t *p_sys = p_access->p_sys;
1879 if( p_sys->i_ca_type & CA_CI_LINK )
1882 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1884 if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
1886 msg_Err( p_access, "en50221_Init: couldn't reset slot %d",
1891 p_sys->i_ca_timeout = 100000;
1892 /* Wait a bit otherwise it doesn't initialize properly... */
1899 struct ca_slot_info info;
1902 /* We don't reset the CAM in that case because it's done by the
1904 if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &info ) < 0 )
1906 msg_Err( p_access, "en50221_Init: couldn't get slot info" );
1907 close( p_sys->i_ca_handle );
1908 p_sys->i_ca_handle = 0;
1909 return VLC_EGENERIC;
1911 if( info.flags == 0 )
1913 msg_Err( p_access, "en50221_Init: no CAM inserted" );
1914 close( p_sys->i_ca_handle );
1915 p_sys->i_ca_handle = 0;
1916 return VLC_EGENERIC;
1919 /* Allocate a dummy sessions */
1920 p_sys->p_sessions[ 0 ].i_resource_id = RI_CONDITIONAL_ACCESS_SUPPORT;
1922 /* Get application info to find out which cam we are using and make
1923 sure everything is ready to play */
1926 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1927 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1928 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1929 memset( &ca_msg.msg[3], 0, 253 );
1930 APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1931 if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1933 msg_Err( p_access, "en50221_Init: failed getting message" );
1934 return VLC_EGENERIC;
1937 #if HLCI_WAIT_CAM_READY
1938 while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1940 if( !vlc_object_alive (p_access) ) return VLC_EGENERIC;
1942 msg_Dbg( p_access, "CAM: please wait" );
1943 APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1945 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1946 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1947 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1948 memset( &ca_msg.msg[3], 0, 253 );
1949 if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1951 msg_Err( p_access, "en50221_Init: failed getting message" );
1952 return VLC_EGENERIC;
1954 msg_Dbg( p_access, "en50221_Init: Got length: %d, tag: 0x%x", ca_msg.length, APDUGetTag( ca_msg.msg, ca_msg.length ) );
1957 if( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1959 msg_Err( p_access, "CAM returns garbage as application info!" );
1960 return VLC_EGENERIC;
1963 msg_Dbg( p_access, "found CAM %s using id 0x%x", &ca_msg.msg[12],
1964 (ca_msg.msg[8]<<8)|ca_msg.msg[9] );
1969 /*****************************************************************************
1970 * en50221_Poll : Poll the CAM for TPDUs
1971 *****************************************************************************/
1972 int en50221_Poll( access_t * p_access )
1974 access_sys_t *p_sys = p_access->p_sys;
1978 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1981 ca_slot_info_t sinfo;
1984 if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
1986 msg_Err( p_access, "en50221_Poll: couldn't get info on slot %d",
1991 if ( !(sinfo.flags & CA_CI_MODULE_READY) )
1993 if ( p_sys->pb_active_slot[i_slot] )
1995 msg_Dbg( p_access, "en50221_Poll: slot %d has been removed",
1997 p_sys->pb_active_slot[i_slot] = false;
1998 p_sys->pb_slot_mmi_expected[i_slot] = false;
1999 p_sys->pb_slot_mmi_undisplayed[i_slot] = false;
2001 /* Close all sessions for this slot. */
2002 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS;
2005 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2006 && p_sys->p_sessions[i_session_id - 1].i_slot
2009 if ( p_sys->p_sessions[i_session_id - 1].pf_close
2012 p_sys->p_sessions[i_session_id - 1].pf_close(
2013 p_access, i_session_id );
2015 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
2022 else if ( !p_sys->pb_active_slot[i_slot] )
2024 InitSlot( p_access, i_slot );
2026 if ( !p_sys->pb_active_slot[i_slot] )
2028 msg_Dbg( p_access, "en50221_Poll: resetting slot %d", i_slot );
2030 if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
2032 msg_Err( p_access, "en50221_Poll: couldn't reset slot %d",
2038 msg_Dbg( p_access, "en50221_Poll: slot %d is active",
2042 if ( !p_sys->pb_tc_has_data[i_slot] )
2044 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, NULL, 0 ) !=
2048 "en50221_Poll: couldn't send TPDU on slot %d",
2052 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) !=
2056 "en50221_Poll: couldn't recv TPDU on slot %d",
2062 while ( p_sys->pb_tc_has_data[i_slot] )
2064 uint8_t p_tpdu[MAX_TPDU_SIZE];
2065 int i_size, i_session_size;
2068 if ( TPDUSend( p_access, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
2071 "en50221_Poll: couldn't send TPDU on slot %d",
2075 if ( TPDURecv( p_access, i_slot, &i_tag, p_tpdu, &i_size ) !=
2079 "en50221_Poll: couldn't recv TPDU on slot %d",
2084 p_session = GetLength( &p_tpdu[3], &i_session_size );
2085 if ( i_session_size <= 1 )
2091 if ( i_tag != T_DATA_LAST )
2094 "en50221_Poll: fragmented TPDU not supported" );
2098 SPDUHandle( p_access, i_slot, p_session, i_session_size );
2102 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2104 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2105 && p_sys->p_sessions[i_session_id - 1].pf_manage )
2107 p_sys->p_sessions[i_session_id - 1].pf_manage( p_access,
2116 /*****************************************************************************
2117 * en50221_SetCAPMT :
2118 *****************************************************************************/
2119 int en50221_SetCAPMT( access_t * p_access, dvbpsi_pmt_t *p_pmt )
2121 access_sys_t *p_sys = p_access->p_sys;
2122 int i, i_session_id;
2123 bool b_update = false;
2124 bool b_needs_descrambling = CAPMTNeedsDescrambling( p_pmt );
2126 for ( i = 0; i < MAX_PROGRAMS; i++ )
2128 if ( p_sys->pp_selected_programs[i] != NULL
2129 && p_sys->pp_selected_programs[i]->i_program_number
2130 == p_pmt->i_program_number )
2134 if ( !b_needs_descrambling )
2136 dvbpsi_DeletePMT( p_pmt );
2137 p_pmt = p_sys->pp_selected_programs[i];
2138 p_sys->pp_selected_programs[i] = NULL;
2140 else if( p_pmt != p_sys->pp_selected_programs[i] )
2142 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
2143 p_sys->pp_selected_programs[i] = p_pmt;
2150 if ( !b_update && b_needs_descrambling )
2152 for ( i = 0; i < MAX_PROGRAMS; i++ )
2154 if ( p_sys->pp_selected_programs[i] == NULL )
2156 p_sys->pp_selected_programs[i] = p_pmt;
2162 if ( b_update || b_needs_descrambling )
2164 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2166 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2167 == RI_CONDITIONAL_ACCESS_SUPPORT )
2169 if ( b_update && b_needs_descrambling )
2170 CAPMTUpdate( p_access, i_session_id, p_pmt );
2171 else if ( b_update )
2172 CAPMTDelete( p_access, i_session_id, p_pmt );
2174 CAPMTAdd( p_access, i_session_id, p_pmt );
2179 if ( !b_needs_descrambling )
2181 dvbpsi_DeletePMT( p_pmt );
2187 /*****************************************************************************
2189 *****************************************************************************/
2190 int en50221_OpenMMI( access_t * p_access, int i_slot )
2192 access_sys_t *p_sys = p_access->p_sys;
2194 if( p_sys->i_ca_type & CA_CI_LINK )
2197 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2199 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2200 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2203 "MMI menu is already opened on slot %d (session=%d)",
2204 i_slot, i_session_id );
2209 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2211 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2212 == RI_APPLICATION_INFORMATION
2213 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2215 ApplicationInformationEnterMenu( p_access, i_session_id );
2220 msg_Err( p_access, "no application information on slot %d", i_slot );
2221 return VLC_EGENERIC;
2225 msg_Err( p_access, "MMI menu not supported" );
2226 return VLC_EGENERIC;
2230 /*****************************************************************************
2231 * en50221_CloseMMI :
2232 *****************************************************************************/
2233 int en50221_CloseMMI( access_t * p_access, int i_slot )
2235 access_sys_t *p_sys = p_access->p_sys;
2237 if( p_sys->i_ca_type & CA_CI_LINK )
2240 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2242 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2243 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2245 MMISendClose( p_access, i_session_id );
2250 msg_Warn( p_access, "closing a non-existing MMI session on slot %d",
2252 return VLC_EGENERIC;
2256 msg_Err( p_access, "MMI menu not supported" );
2257 return VLC_EGENERIC;
2261 /*****************************************************************************
2262 * en50221_GetMMIObject :
2263 *****************************************************************************/
2264 en50221_mmi_object_t *en50221_GetMMIObject( access_t * p_access,
2267 access_sys_t *p_sys = p_access->p_sys;
2270 if ( p_sys->pb_slot_mmi_expected[i_slot] == true )
2271 return NULL; /* should not happen */
2273 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2275 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2276 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2279 (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
2280 if ( p_mmi == NULL )
2281 return NULL; /* should not happen */
2282 return &p_mmi->last_object;
2290 /*****************************************************************************
2291 * en50221_SendMMIObject :
2292 *****************************************************************************/
2293 void en50221_SendMMIObject( access_t * p_access, int i_slot,
2294 en50221_mmi_object_t *p_object )
2296 access_sys_t *p_sys = p_access->p_sys;
2299 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2301 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2302 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2304 MMISendObject( p_access, i_session_id, p_object );
2309 msg_Err( p_access, "SendMMIObject when no MMI session is opened !" );
2312 /*****************************************************************************
2314 *****************************************************************************/
2315 void en50221_End( access_t * p_access )
2317 access_sys_t *p_sys = p_access->p_sys;
2318 int i_session_id, i;
2320 for ( i = 0; i < MAX_PROGRAMS; i++ )
2322 if ( p_sys->pp_selected_programs[i] != NULL )
2324 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
2328 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2330 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2331 && p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
2333 p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
2338 /* Leave the CAM configured, so that it can be reused in another
2342 static inline void *FixUTF8( char *p )
2348 char *dvbsi_to_utf8( const char *psz_instring, size_t i_length )
2350 const char *psz_encoding, *psz_stringstart;
2351 char *psz_outstring, *psz_tmp;
2352 char psz_encbuf[12];
2354 vlc_iconv_t iconv_handle;
2355 if( i_length < 1 ) return NULL;
2356 if( psz_instring[0] < 0 || psz_instring[0] >= 0x20 )
2358 psz_stringstart = psz_instring;
2359 psz_encoding = "ISO_8859-1"; /* should be ISO6937 according to spec, but this seems to be the one used */
2360 } else switch( psz_instring[0] )
2363 psz_stringstart = &psz_instring[1];
2364 psz_encoding = "ISO_8859-5";
2367 psz_stringstart = &psz_instring[1];
2368 psz_encoding = "ISO_8859-6";
2371 psz_stringstart = &psz_instring[1];
2372 psz_encoding = "ISO_8859-7";
2375 psz_stringstart = &psz_instring[1];
2376 psz_encoding = "ISO_8859-8";
2379 psz_stringstart = &psz_instring[1];
2380 psz_encoding = "ISO_8859-9";
2383 psz_stringstart = &psz_instring[1];
2384 psz_encoding = "ISO_8859-10";
2387 psz_stringstart = &psz_instring[1];
2388 psz_encoding = "ISO_8859-11";
2391 psz_stringstart = &psz_instring[1]; /*possibly reserved?*/
2392 psz_encoding = "ISO_8859-12";
2395 psz_stringstart = &psz_instring[1];
2396 psz_encoding = "ISO_8859-13";
2399 psz_stringstart = &psz_instring[1];
2400 psz_encoding = "ISO_8859-14";
2403 psz_stringstart = &psz_instring[1];
2404 psz_encoding = "ISO_8859-15";
2407 if( i_length < 3 || psz_instring[1] != '\0' || psz_instring[2] > 0x0f
2408 || psz_instring[2] == 0 )
2409 return FixUTF8(strndup(psz_instring,i_length));
2410 sprintf( psz_encbuf, "ISO_8859-%d", psz_instring[2] );
2411 psz_stringstart = &psz_instring[3];
2412 psz_encoding = psz_encbuf;
2415 psz_stringstart = &psz_instring[1];
2416 psz_encoding = "UTF-16";
2419 psz_stringstart = &psz_instring[1];
2420 psz_encoding = "KSC5601-1987";
2423 psz_stringstart = &psz_instring[1];
2424 psz_encoding = "GB2312";/*GB-2312-1980 */
2427 psz_stringstart = &psz_instring[1];
2428 psz_encoding = "BIG-5";
2431 return FixUTF8(strndup(&psz_instring[1],i_length-1));
2435 return FixUTF8(strndup(psz_instring,i_length));
2437 iconv_handle = vlc_iconv_open( "UTF-8", psz_encoding );
2438 i_in = i_length - (psz_stringstart - psz_instring );
2440 psz_outstring = psz_tmp = (char*)xmalloc( i_out + 1 );
2441 vlc_iconv( iconv_handle, &psz_stringstart, &i_in, &psz_tmp, &i_out );
2442 vlc_iconv_close( iconv_handle );
2444 return psz_outstring;