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>
42 #include <netinet/in.h>
44 /* DVB Card Drivers */
45 #include <linux/dvb/version.h>
46 #include <linux/dvb/dmx.h>
47 #include <linux/dvb/frontend.h>
48 #include <linux/dvb/ca.h>
50 /* Include dvbpsi headers */
51 #ifdef HAVE_DVBPSI_DR_H
52 # include <dvbpsi/dvbpsi.h>
53 # include <dvbpsi/descriptor.h>
54 # include <dvbpsi/pat.h>
55 # include <dvbpsi/pmt.h>
56 # include <dvbpsi/dr.h>
57 # include <dvbpsi/psi.h>
58 # include <dvbpsi/demux.h>
59 # include <dvbpsi/sdt.h>
62 # include "descriptor.h"
63 # include "tables/pat.h"
64 # include "tables/pmt.h"
65 # include "descriptors/dr.h"
68 # include "tables/sdt.h"
72 # include <vlc_httpd.h>
77 #include <vlc_charset.h>
80 #define HLCI_WAIT_CAM_READY 0
81 #define CAM_PROG_MAX MAX_PROGRAMS
82 //#define CAPMT_WAIT 100 /* uncomment this for slow CAMs */
84 static void ResourceManagerOpen( access_t * p_access, int i_session_id );
85 static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
86 static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
87 static void DateTimeOpen( access_t * p_access, int i_session_id );
88 static void MMIOpen( access_t * p_access, int i_session_id );
90 /*****************************************************************************
92 *****************************************************************************/
93 #define SIZE_INDICATOR 0x80
95 static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
97 *pi_length = *p_data++;
99 if ( (*pi_length & SIZE_INDICATOR) != 0 )
101 int l = *pi_length & ~SIZE_INDICATOR;
105 for ( i = 0; i < l; i++ )
106 *pi_length = (*pi_length << 8) | *p_data++;
112 static uint8_t *SetLength( uint8_t *p_data, int i_length )
116 if ( i_length < 128 )
120 else if ( i_length < 256 )
122 *p++ = SIZE_INDICATOR | 0x1;
125 else if ( i_length < 65536 )
127 *p++ = SIZE_INDICATOR | 0x2;
128 *p++ = i_length >> 8;
129 *p++ = i_length & 0xff;
131 else if ( i_length < 16777216 )
133 *p++ = SIZE_INDICATOR | 0x3;
134 *p++ = i_length >> 16;
135 *p++ = (i_length >> 8) & 0xff;
136 *p++ = i_length & 0xff;
140 *p++ = SIZE_INDICATOR | 0x4;
141 *p++ = i_length >> 24;
142 *p++ = (i_length >> 16) & 0xff;
143 *p++ = (i_length >> 8) & 0xff;
144 *p++ = i_length & 0xff;
155 #define MAX_TPDU_SIZE 4096
156 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
158 #define DATA_INDICATOR 0x80
162 #define T_CREATE_TC 0x82
163 #define T_CTC_REPLY 0x83
164 #define T_DELETE_TC 0x84
165 #define T_DTC_REPLY 0x85
166 #define T_REQUEST_TC 0x86
167 #define T_NEW_TC 0x87
168 #define T_TC_ERROR 0x88
169 #define T_DATA_LAST 0xA0
170 #define T_DATA_MORE 0xA1
172 static void Dump( bool b_outgoing, uint8_t *p_data, int i_size )
177 fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
178 for ( i = 0; i < i_size && i < MAX_DUMP; i++)
179 fprintf(stderr, "%02X ", p_data[i]);
180 fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
182 VLC_UNUSED(b_outgoing); VLC_UNUSED(p_data); VLC_UNUSED(i_size);
186 /*****************************************************************************
188 *****************************************************************************/
189 static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
190 const uint8_t *p_content, int i_length )
192 access_sys_t *p_sys = p_access->p_sys;
193 uint8_t i_tcid = i_slot + 1;
194 uint8_t p_data[MAX_TPDU_SIZE];
210 p_data[3] = 1; /* length */
217 p_data[3] = 2; /* length */
219 p_data[5] = p_content[0];
226 /* i_length <= MAX_TPDU_DATA */
227 uint8_t *p = p_data + 3;
228 p = SetLength( p, i_length + 1 );
232 memcpy( p, p_content, i_length );
233 i_size = i_length + (p - p_data);
240 Dump( true, p_data, i_size );
242 if ( write( p_sys->i_ca_handle, p_data, i_size ) != i_size )
244 msg_Err( p_access, "cannot write to CAM device (%m)" );
252 /*****************************************************************************
254 *****************************************************************************/
255 #define CAM_READ_TIMEOUT 3500 // ms
257 static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
258 uint8_t *p_data, int *pi_size )
260 access_sys_t *p_sys = p_access->p_sys;
261 uint8_t i_tcid = i_slot + 1;
263 struct pollfd pfd[1];
265 pfd[0].fd = p_sys->i_ca_handle;
266 pfd[0].events = POLLIN;
267 if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
269 msg_Err( p_access, "cannot poll from CAM device" );
273 if ( pi_size == NULL )
275 p_data = malloc( MAX_TPDU_SIZE );
281 i_size = read( p_sys->i_ca_handle, p_data, MAX_TPDU_SIZE );
283 if ( i_size >= 0 || errno != EINTR )
289 msg_Err( p_access, "cannot read from CAM device (%d:%m)", i_size );
290 if( pi_size == NULL )
295 if ( p_data[1] != i_tcid )
297 msg_Err( p_access, "invalid read from CAM device (%d instead of %d)",
299 if( pi_size == NULL )
305 p_sys->pb_tc_has_data[i_slot] = (i_size >= 4
306 && p_data[i_size - 4] == T_SB
307 && p_data[i_size - 3] == 2
308 && (p_data[i_size - 1] & DATA_INDICATOR))
311 Dump( false, p_data, i_size );
313 if ( pi_size == NULL )
326 #define ST_SESSION_NUMBER 0x90
327 #define ST_OPEN_SESSION_REQUEST 0x91
328 #define ST_OPEN_SESSION_RESPONSE 0x92
329 #define ST_CREATE_SESSION 0x93
330 #define ST_CREATE_SESSION_RESPONSE 0x94
331 #define ST_CLOSE_SESSION_REQUEST 0x95
332 #define ST_CLOSE_SESSION_RESPONSE 0x96
335 #define SS_NOT_ALLOCATED 0xF0
337 #define RI_RESOURCE_MANAGER 0x00010041
338 #define RI_APPLICATION_INFORMATION 0x00020041
339 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
340 #define RI_HOST_CONTROL 0x00200041
341 #define RI_DATE_TIME 0x00240041
342 #define RI_MMI 0x00400041
344 static int ResourceIdToInt( uint8_t *p_data )
346 return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
347 | ((int)p_data[2] << 8) | p_data[3];
350 /*****************************************************************************
352 *****************************************************************************/
353 static int SPDUSend( access_t * p_access, int i_session_id,
354 uint8_t *p_data, int i_size )
356 access_sys_t *p_sys = p_access->p_sys;
357 uint8_t *p_spdu = malloc( i_size + 4 );
360 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
364 *p++ = ST_SESSION_NUMBER;
366 *p++ = (i_session_id >> 8);
367 *p++ = i_session_id & 0xff;
369 memcpy( p, p_data, i_size );
376 if ( i_size > MAX_TPDU_DATA )
378 if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
379 MAX_TPDU_DATA ) != VLC_SUCCESS )
381 msg_Err( p_access, "couldn't send TPDU on session %d",
387 i_size -= MAX_TPDU_DATA;
391 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
394 msg_Err( p_access, "couldn't send TPDU on session %d",
402 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
405 msg_Err( p_access, "couldn't recv TPDU on session %d",
416 /*****************************************************************************
418 *****************************************************************************/
419 static void SessionOpen( access_t * p_access, uint8_t i_slot,
420 uint8_t *p_spdu, int i_size )
422 VLC_UNUSED( i_size );
424 access_sys_t *p_sys = p_access->p_sys;
426 int i_resource_id = ResourceIdToInt( &p_spdu[2] );
427 uint8_t p_response[16];
428 int i_status = SS_NOT_ALLOCATED;
431 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
433 if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
436 if ( i_session_id > MAX_SESSIONS )
438 msg_Err( p_access, "too many sessions !" );
441 p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
442 p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
443 p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
444 p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
446 if ( i_resource_id == RI_RESOURCE_MANAGER
447 || i_resource_id == RI_APPLICATION_INFORMATION
448 || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
449 || i_resource_id == RI_DATE_TIME
450 || i_resource_id == RI_MMI )
455 p_response[0] = ST_OPEN_SESSION_RESPONSE;
457 p_response[2] = i_status;
458 p_response[3] = p_spdu[2];
459 p_response[4] = p_spdu[3];
460 p_response[5] = p_spdu[4];
461 p_response[6] = p_spdu[5];
462 p_response[7] = i_session_id >> 8;
463 p_response[8] = i_session_id & 0xff;
465 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) !=
469 "SessionOpen: couldn't send TPDU on slot %d", i_slot );
472 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
475 "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
479 switch ( i_resource_id )
481 case RI_RESOURCE_MANAGER:
482 ResourceManagerOpen( p_access, i_session_id ); break;
483 case RI_APPLICATION_INFORMATION:
484 ApplicationInformationOpen( p_access, i_session_id ); break;
485 case RI_CONDITIONAL_ACCESS_SUPPORT:
486 ConditionalAccessOpen( p_access, i_session_id ); break;
488 DateTimeOpen( p_access, i_session_id ); break;
490 MMIOpen( p_access, i_session_id ); break;
492 case RI_HOST_CONTROL:
494 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
495 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
500 /* unused code for the moment - commented out to keep gcc happy */
501 /*****************************************************************************
503 *****************************************************************************/
504 static void SessionCreate( access_t * p_access, int i_slot, int i_resource_id )
506 access_sys_t *p_sys = p_access->p_sys;
507 uint8_t p_response[16];
511 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
513 if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
516 if ( i_session_id == MAX_SESSIONS )
518 msg_Err( p_access, "too many sessions !" );
521 p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
522 p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
523 p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
524 p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
525 p_sys->p_sessions[i_session_id - 1].p_sys = NULL;
527 p_response[0] = ST_CREATE_SESSION;
529 p_response[2] = i_resource_id >> 24;
530 p_response[3] = (i_resource_id >> 16) & 0xff;
531 p_response[4] = (i_resource_id >> 8) & 0xff;
532 p_response[5] = i_resource_id & 0xff;
533 p_response[6] = i_session_id >> 8;
534 p_response[7] = i_session_id & 0xff;
536 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
540 "SessionCreate: couldn't send TPDU on slot %d", i_slot );
543 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
546 "SessionCreate: couldn't recv TPDU on slot %d", i_slot );
552 /*****************************************************************************
553 * SessionCreateResponse
554 *****************************************************************************/
555 static void SessionCreateResponse( access_t * p_access, uint8_t i_slot,
556 uint8_t *p_spdu, int i_size )
558 VLC_UNUSED( i_size );
559 VLC_UNUSED( i_slot );
561 access_sys_t *p_sys = p_access->p_sys;
562 int i_status = p_spdu[2];
563 int i_resource_id = ResourceIdToInt( &p_spdu[3] );
564 int i_session_id = ((int)p_spdu[7] << 8) | p_spdu[8];
566 if ( i_status != SS_OK )
568 msg_Err( p_access, "SessionCreateResponse: failed to open session %d"
569 " resource=0x%x status=0x%x", i_session_id, i_resource_id,
571 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
575 switch ( i_resource_id )
577 case RI_RESOURCE_MANAGER:
578 ResourceManagerOpen( p_access, i_session_id ); break;
579 case RI_APPLICATION_INFORMATION:
580 ApplicationInformationOpen( p_access, i_session_id ); break;
581 case RI_CONDITIONAL_ACCESS_SUPPORT:
582 ConditionalAccessOpen( p_access, i_session_id ); break;
584 DateTimeOpen( p_access, i_session_id ); break;
586 MMIOpen( p_access, i_session_id ); break;
588 case RI_HOST_CONTROL:
590 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
591 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
595 /*****************************************************************************
597 *****************************************************************************/
598 static void SessionSendClose( access_t * p_access, int i_session_id )
600 access_sys_t *p_sys = p_access->p_sys;
601 uint8_t p_response[16];
603 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
605 p_response[0] = ST_CLOSE_SESSION_REQUEST;
607 p_response[2] = i_session_id >> 8;
608 p_response[3] = i_session_id & 0xff;
610 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
614 "SessionSendClose: couldn't send TPDU on slot %d", i_slot );
617 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
620 "SessionSendClose: couldn't recv TPDU on slot %d", i_slot );
625 /*****************************************************************************
627 *****************************************************************************/
628 static void SessionClose( access_t * p_access, int i_session_id )
630 access_sys_t *p_sys = p_access->p_sys;
631 uint8_t p_response[16];
633 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
635 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
636 p_sys->p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
637 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
639 p_response[0] = ST_CLOSE_SESSION_RESPONSE;
641 p_response[2] = SS_OK;
642 p_response[3] = i_session_id >> 8;
643 p_response[4] = i_session_id & 0xff;
645 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
649 "SessionClose: couldn't send TPDU on slot %d", i_slot );
652 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
655 "SessionClose: couldn't recv TPDU on slot %d", i_slot );
660 /*****************************************************************************
662 *****************************************************************************/
663 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
664 uint8_t *p_spdu, int i_size )
666 access_sys_t *p_sys = p_access->p_sys;
671 case ST_SESSION_NUMBER:
674 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
675 p_sys->p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
676 p_spdu + 4, i_size - 4 );
679 case ST_OPEN_SESSION_REQUEST:
680 if ( i_size != 6 || p_spdu[1] != 0x4 )
682 SessionOpen( p_access, i_slot, p_spdu, i_size );
685 case ST_CREATE_SESSION_RESPONSE:
686 if ( i_size != 9 || p_spdu[1] != 0x7 )
688 SessionCreateResponse( p_access, i_slot, p_spdu, i_size );
691 case ST_CLOSE_SESSION_REQUEST:
692 if ( i_size != 4 || p_spdu[1] != 0x2 )
694 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
695 SessionClose( p_access, i_session_id );
698 case ST_CLOSE_SESSION_RESPONSE:
699 if ( i_size != 5 || p_spdu[1] != 0x3 )
701 i_session_id = ((int)p_spdu[3] << 8) | p_spdu[4];
704 msg_Err( p_access, "closing a session which is not allocated (%d)",
709 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
710 p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
712 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
717 msg_Err( p_access, "unexpected tag in SPDUHandle (%x)", p_spdu[0] );
727 #define AOT_NONE 0x000000
728 #define AOT_PROFILE_ENQ 0x9F8010
729 #define AOT_PROFILE 0x9F8011
730 #define AOT_PROFILE_CHANGE 0x9F8012
731 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
732 #define AOT_APPLICATION_INFO 0x9F8021
733 #define AOT_ENTER_MENU 0x9F8022
734 #define AOT_CA_INFO_ENQ 0x9F8030
735 #define AOT_CA_INFO 0x9F8031
736 #define AOT_CA_PMT 0x9F8032
737 #define AOT_CA_PMT_REPLY 0x9F8033
738 #define AOT_TUNE 0x9F8400
739 #define AOT_REPLACE 0x9F8401
740 #define AOT_CLEAR_REPLACE 0x9F8402
741 #define AOT_ASK_RELEASE 0x9F8403
742 #define AOT_DATE_TIME_ENQ 0x9F8440
743 #define AOT_DATE_TIME 0x9F8441
744 #define AOT_CLOSE_MMI 0x9F8800
745 #define AOT_DISPLAY_CONTROL 0x9F8801
746 #define AOT_DISPLAY_REPLY 0x9F8802
747 #define AOT_TEXT_LAST 0x9F8803
748 #define AOT_TEXT_MORE 0x9F8804
749 #define AOT_KEYPAD_CONTROL 0x9F8805
750 #define AOT_KEYPRESS 0x9F8806
751 #define AOT_ENQ 0x9F8807
752 #define AOT_ANSW 0x9F8808
753 #define AOT_MENU_LAST 0x9F8809
754 #define AOT_MENU_MORE 0x9F880A
755 #define AOT_MENU_ANSW 0x9F880B
756 #define AOT_LIST_LAST 0x9F880C
757 #define AOT_LIST_MORE 0x9F880D
758 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
759 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
760 #define AOT_DISPLAY_MESSAGE 0x9F8810
761 #define AOT_SCENE_END_MARK 0x9F8811
762 #define AOT_SCENE_DONE 0x9F8812
763 #define AOT_SCENE_CONTROL 0x9F8813
764 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
765 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
766 #define AOT_FLUSH_DOWNLOAD 0x9F8816
767 #define AOT_DOWNLOAD_REPLY 0x9F8817
768 #define AOT_COMMS_CMD 0x9F8C00
769 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
770 #define AOT_COMMS_REPLY 0x9F8C02
771 #define AOT_COMMS_SEND_LAST 0x9F8C03
772 #define AOT_COMMS_SEND_MORE 0x9F8C04
773 #define AOT_COMMS_RCV_LAST 0x9F8C05
774 #define AOT_COMMS_RCV_MORE 0x9F8C06
776 /*****************************************************************************
778 *****************************************************************************/
779 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
784 for ( i = 0; i < 3; i++ )
785 t = (t << 8) | *p_apdu++;
792 /*****************************************************************************
794 *****************************************************************************/
795 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
797 return GetLength( &p_apdu[3], pi_size );
800 /*****************************************************************************
802 *****************************************************************************/
803 static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
804 uint8_t *p_data, int i_size )
806 access_sys_t *p_sys = p_access->p_sys;
807 uint8_t *p_apdu = malloc( i_size + 12 );
814 *p++ = (i_tag >> 16);
815 *p++ = (i_tag >> 8) & 0xff;
817 p = SetLength( p, i_size );
819 memcpy( p, p_data, i_size );
820 if ( p_sys->i_ca_type == CA_CI_LINK )
822 i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
826 if ( i_size + p - p_apdu > 256 )
828 msg_Err( p_access, "CAM: apdu overflow" );
829 i_ret = VLC_EGENERIC;
833 ca_msg.length = i_size + p - p_apdu;
834 if ( i_size == 0 ) ca_msg.length=3;
835 memcpy( ca_msg.msg, p_apdu, i_size + p - p_apdu );
836 i_ret = ioctl(p_sys->i_ca_handle, CA_SEND_MSG, &ca_msg );
839 msg_Err( p_access, "Error sending to CAM: %m" );
840 i_ret = VLC_EGENERIC;
852 /*****************************************************************************
853 * ResourceManagerHandle
854 *****************************************************************************/
855 static void ResourceManagerHandle( access_t * p_access, int i_session_id,
856 uint8_t *p_apdu, int i_size )
858 int i_tag = APDUGetTag( p_apdu, i_size );
862 case AOT_PROFILE_ENQ:
864 int resources[] = { htonl(RI_RESOURCE_MANAGER),
865 htonl(RI_APPLICATION_INFORMATION),
866 htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
870 APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
875 APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
879 msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
884 /*****************************************************************************
885 * ResourceManagerOpen
886 *****************************************************************************/
887 static void ResourceManagerOpen( access_t * p_access, int i_session_id )
889 access_sys_t *p_sys = p_access->p_sys;
891 msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
893 p_sys->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
895 APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
899 * Application Information
902 /*****************************************************************************
903 * ApplicationInformationEnterMenu
904 *****************************************************************************/
905 static void ApplicationInformationEnterMenu( access_t * p_access,
908 access_sys_t *p_sys = p_access->p_sys;
909 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
911 msg_Dbg( p_access, "entering MMI menus on session %d", i_session_id );
912 APDUSend( p_access, i_session_id, AOT_ENTER_MENU, NULL, 0 );
913 p_sys->pb_slot_mmi_expected[i_slot] = true;
916 /*****************************************************************************
917 * ApplicationInformationHandle
918 *****************************************************************************/
919 static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
920 uint8_t *p_apdu, int i_size )
922 VLC_UNUSED(i_session_id);
923 int i_tag = APDUGetTag( p_apdu, i_size );
927 case AOT_APPLICATION_INFO:
929 int i_type, i_manufacturer, i_code;
931 uint8_t *d = APDUGetLength( p_apdu, &l );
934 p_apdu[l + 4] = '\0';
937 i_manufacturer = ((int)d[0] << 8) | d[1];
939 i_code = ((int)d[0] << 8) | d[1];
941 d = GetLength( d, &l );
943 msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
944 d, i_type, i_manufacturer, i_code );
949 "unexpected tag in ApplicationInformationHandle (0x%x)",
954 /*****************************************************************************
955 * ApplicationInformationOpen
956 *****************************************************************************/
957 static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
959 access_sys_t *p_sys = p_access->p_sys;
961 msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
963 p_sys->p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
965 APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
972 #define MAX_CASYSTEM_IDS 64
976 uint16_t pi_system_ids[MAX_CASYSTEM_IDS + 1];
979 static bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
982 if( !p_ids ) return true; /* dummy session for high-level CI intf */
984 while ( p_ids->pi_system_ids[i] )
986 if ( p_ids->pi_system_ids[i] == i_id )
994 /*****************************************************************************
995 * CAPMTNeedsDescrambling
996 *****************************************************************************/
997 static bool CAPMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
999 dvbpsi_descriptor_t *p_dr;
1000 dvbpsi_pmt_es_t *p_es;
1002 for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
1004 if( p_dr->i_tag == 0x9 )
1010 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1012 for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
1013 p_dr = p_dr->p_next )
1015 if( p_dr->i_tag == 0x9 )
1025 /*****************************************************************************
1027 *****************************************************************************/
1028 static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr )
1032 while ( p_dr != NULL )
1034 if( p_dr->i_tag == 0x9 )
1036 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1038 if ( CheckSystemID( p_ids, i_sysid ) )
1039 i_cad_size += p_dr->i_length + 2;
1041 p_dr = p_dr->p_next;
1047 static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt,
1048 uint16_t i_program_number, uint8_t i_version,
1049 int i_size, dvbpsi_descriptor_t *p_dr,
1055 p_data = malloc( 7 + i_size );
1057 p_data = malloc( 6 );
1061 p_data[0] = i_list_mgt;
1062 p_data[1] = i_program_number >> 8;
1063 p_data[2] = i_program_number & 0xff;
1064 p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
1070 p_data[4] = (i_size + 1) >> 8;
1071 p_data[5] = (i_size + 1) & 0xff;
1075 while ( p_dr != NULL )
1077 if( p_dr->i_tag == 0x9 )
1079 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1081 if ( CheckSystemID( p_ids, i_sysid ) )
1084 p_data[i + 1] = p_dr->i_length;
1085 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1086 // p_data[i+4] &= 0x1f;
1087 i += p_dr->i_length + 2;
1090 p_dr = p_dr->p_next;
1102 static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt,
1103 int i_capmt_size, uint8_t i_type, uint16_t i_pid,
1104 int i_size, dvbpsi_descriptor_t *p_dr,
1111 p_data = realloc( p_capmt, i_capmt_size + 6 + i_size );
1113 p_data = realloc( p_capmt, i_capmt_size + 5 );
1120 p_data[i + 1] = i_pid >> 8;
1121 p_data[i + 2] = i_pid & 0xff;
1125 p_data[i + 3] = (i_size + 1) >> 8;
1126 p_data[i + 4] = (i_size + 1) & 0xff;
1127 p_data[i + 5] = i_cmd;
1130 while ( p_dr != NULL )
1132 if( p_dr->i_tag == 0x9 )
1134 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1136 if ( CheckSystemID( p_ids, i_sysid ) )
1139 p_data[i + 1] = p_dr->i_length;
1140 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1141 i += p_dr->i_length + 2;
1144 p_dr = p_dr->p_next;
1156 static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id,
1157 dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt,
1158 uint8_t i_cmd, int *pi_capmt_size )
1160 access_sys_t *p_sys = p_access->p_sys;
1161 system_ids_t *p_ids =
1162 (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1163 dvbpsi_pmt_es_t *p_es;
1164 int i_cad_size, i_cad_program_size;
1167 i_cad_size = i_cad_program_size =
1168 GetCADSize( p_ids, p_pmt->p_first_descriptor );
1169 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1171 i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor );
1177 "no compatible scrambling system for SID %d on session %d",
1178 p_pmt->i_program_number, i_session_id );
1183 p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number,
1184 p_pmt->i_version, i_cad_program_size,
1185 p_pmt->p_first_descriptor, i_cmd );
1187 if ( i_cad_program_size )
1188 *pi_capmt_size = 7 + i_cad_program_size;
1192 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1194 i_cad_size = GetCADSize( p_ids, p_es->p_first_descriptor );
1196 if ( i_cad_size || i_cad_program_size )
1198 p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type,
1199 p_es->i_pid, i_cad_size,
1200 p_es->p_first_descriptor, i_cmd );
1202 *pi_capmt_size += 6 + i_cad_size;
1204 *pi_capmt_size += 5;
1211 /*****************************************************************************
1213 *****************************************************************************/
1214 static void CAPMTFirst( access_t * p_access, int i_session_id,
1215 dvbpsi_pmt_t *p_pmt )
1220 msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d",
1221 p_pmt->i_program_number, i_session_id );
1223 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1224 0x3 /* only */, 0x1 /* ok_descrambling */,
1229 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1234 /*****************************************************************************
1236 *****************************************************************************/
1237 static void CAPMTAdd( access_t * p_access, int i_session_id,
1238 dvbpsi_pmt_t *p_pmt )
1243 if( p_access->p_sys->i_selected_programs >= CAM_PROG_MAX )
1245 msg_Warn( p_access, "Not adding CAPMT for SID %d, too many programs",
1246 p_pmt->i_program_number );
1249 p_access->p_sys->i_selected_programs++;
1250 if( p_access->p_sys->i_selected_programs == 1 )
1252 CAPMTFirst( p_access, i_session_id, p_pmt );
1257 msleep( CAPMT_WAIT * 1000 );
1260 msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
1261 p_pmt->i_program_number, i_session_id );
1263 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1264 0x4 /* add */, 0x1 /* ok_descrambling */,
1269 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1274 /*****************************************************************************
1276 *****************************************************************************/
1277 static void CAPMTUpdate( access_t * p_access, int i_session_id,
1278 dvbpsi_pmt_t *p_pmt )
1283 msg_Dbg( p_access, "updating CAPMT for SID %d on session %d",
1284 p_pmt->i_program_number, i_session_id );
1286 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1287 0x5 /* update */, 0x1 /* ok_descrambling */,
1292 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1297 /*****************************************************************************
1299 *****************************************************************************/
1300 static void CAPMTDelete( access_t * p_access, int i_session_id,
1301 dvbpsi_pmt_t *p_pmt )
1306 p_access->p_sys->i_selected_programs--;
1307 msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d",
1308 p_pmt->i_program_number, i_session_id );
1310 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1311 0x5 /* update */, 0x4 /* not selected */,
1316 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1321 /*****************************************************************************
1322 * ConditionalAccessHandle
1323 *****************************************************************************/
1324 static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
1325 uint8_t *p_apdu, int i_size )
1327 access_sys_t *p_sys = p_access->p_sys;
1328 system_ids_t *p_ids =
1329 (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1330 int i_tag = APDUGetTag( p_apdu, i_size );
1338 uint8_t *d = APDUGetLength( p_apdu, &l );
1339 msg_Dbg( p_access, "CA system IDs supported by the application :" );
1341 for ( i = 0; i < l / 2; i++ )
1343 p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
1345 msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] );
1347 p_ids->pi_system_ids[i] = 0;
1349 for ( i = 0; i < MAX_PROGRAMS; i++ )
1351 if ( p_sys->pp_selected_programs[i] != NULL )
1353 CAPMTAdd( p_access, i_session_id,
1354 p_sys->pp_selected_programs[i] );
1362 "unexpected tag in ConditionalAccessHandle (0x%x)",
1367 /*****************************************************************************
1368 * ConditionalAccessClose
1369 *****************************************************************************/
1370 static void ConditionalAccessClose( access_t * p_access, int i_session_id )
1372 access_sys_t *p_sys = p_access->p_sys;
1374 msg_Dbg( p_access, "closing ConditionalAccess session (%d)", i_session_id );
1376 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1379 /*****************************************************************************
1380 * ConditionalAccessOpen
1381 *****************************************************************************/
1382 static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
1384 access_sys_t *p_sys = p_access->p_sys;
1386 msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
1388 p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
1389 p_sys->p_sessions[i_session_id - 1].pf_close = ConditionalAccessClose;
1390 p_sys->p_sessions[i_session_id - 1].p_sys = calloc( 1, sizeof(system_ids_t) );
1392 APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
1405 /*****************************************************************************
1407 *****************************************************************************/
1408 static void DateTimeSend( access_t * p_access, int i_session_id )
1410 access_sys_t *p_sys = p_access->p_sys;
1411 date_time_t *p_date =
1412 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1414 time_t t = time(NULL);
1418 if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
1420 int Y = tm_gmt.tm_year;
1421 int M = tm_gmt.tm_mon + 1;
1422 int D = tm_gmt.tm_mday;
1423 int L = (M == 1 || M == 2) ? 1 : 0;
1424 int MJD = 14956 + D + (int)((Y - L) * 365.25)
1425 + (int)((M + 1 + L * 12) * 30.6001);
1426 uint8_t p_response[7];
1428 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
1430 p_response[0] = htons(MJD) >> 8;
1431 p_response[1] = htons(MJD) & 0xff;
1432 p_response[2] = DEC2BCD(tm_gmt.tm_hour);
1433 p_response[3] = DEC2BCD(tm_gmt.tm_min);
1434 p_response[4] = DEC2BCD(tm_gmt.tm_sec);
1435 p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
1436 p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
1438 APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
1440 p_date->i_last = mdate();
1444 /*****************************************************************************
1446 *****************************************************************************/
1447 static void DateTimeHandle( access_t * p_access, int i_session_id,
1448 uint8_t *p_apdu, int i_size )
1450 access_sys_t *p_sys = p_access->p_sys;
1451 date_time_t *p_date =
1452 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1454 int i_tag = APDUGetTag( p_apdu, i_size );
1458 case AOT_DATE_TIME_ENQ:
1461 const uint8_t *d = APDUGetLength( p_apdu, &l );
1465 p_date->i_interval = *d;
1466 msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
1467 p_date->i_interval );
1470 p_date->i_interval = 0;
1472 DateTimeSend( p_access, i_session_id );
1476 msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
1480 /*****************************************************************************
1482 *****************************************************************************/
1483 static void DateTimeManage( access_t * p_access, int i_session_id )
1485 access_sys_t *p_sys = p_access->p_sys;
1486 date_time_t *p_date =
1487 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1489 if ( p_date->i_interval
1490 && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
1492 DateTimeSend( p_access, i_session_id );
1496 /*****************************************************************************
1498 *****************************************************************************/
1499 static void DateTimeClose( access_t * p_access, int i_session_id )
1501 access_sys_t *p_sys = p_access->p_sys;
1503 msg_Dbg( p_access, "closing DateTime session (%d)", i_session_id );
1505 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1508 /*****************************************************************************
1510 *****************************************************************************/
1511 static void DateTimeOpen( access_t * p_access, int i_session_id )
1513 access_sys_t *p_sys = p_access->p_sys;
1515 msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
1517 p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
1518 p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
1519 p_sys->p_sessions[i_session_id - 1].pf_close = DateTimeClose;
1520 p_sys->p_sessions[i_session_id - 1].p_sys = calloc( 1, sizeof(date_time_t) );
1522 DateTimeSend( p_access, i_session_id );
1529 /* Display Control Commands */
1531 #define DCC_SET_MMI_MODE 0x01
1532 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
1533 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
1534 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
1535 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
1539 #define MM_HIGH_LEVEL 0x01
1540 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
1541 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
1543 /* Display Reply IDs */
1545 #define DRI_MMI_MODE_ACK 0x01
1546 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
1547 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
1548 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
1549 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
1550 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
1551 #define DRI_UNKNOWN_MMI_MODE 0xF1
1552 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
1556 #define EF_BLIND 0x01
1560 #define AI_CANCEL 0x00
1561 #define AI_ANSWER 0x01
1565 en50221_mmi_object_t last_object;
1568 /*****************************************************************************
1570 *****************************************************************************/
1571 static void MMISendObject( access_t *p_access, int i_session_id,
1572 en50221_mmi_object_t *p_object )
1574 access_sys_t *p_sys = p_access->p_sys;
1575 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1579 switch ( p_object->i_object_type )
1581 case EN50221_MMI_ANSW:
1583 i_size = 1 + strlen( p_object->u.answ.psz_answ );
1584 p_data = malloc( i_size );
1586 p_data[0] = (p_object->u.answ.b_ok == true) ? 0x1 : 0x0;
1587 strncpy( (char *)&p_data[1], p_object->u.answ.psz_answ, i_size - 1 );
1590 case EN50221_MMI_MENU_ANSW:
1591 i_tag = AOT_MENU_ANSW;
1593 p_data = malloc( i_size );
1595 p_data[0] = p_object->u.menu_answ.i_choice;
1599 msg_Err( p_access, "unknown MMI object %d", p_object->i_object_type );
1603 APDUSend( p_access, i_session_id, i_tag, p_data, i_size );
1606 p_sys->pb_slot_mmi_expected[i_slot] = true;
1609 /*****************************************************************************
1611 *****************************************************************************/
1612 static void MMISendClose( access_t *p_access, int i_session_id )
1614 access_sys_t *p_sys = p_access->p_sys;
1615 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1617 APDUSend( p_access, i_session_id, AOT_CLOSE_MMI, NULL, 0 );
1619 p_sys->pb_slot_mmi_expected[i_slot] = true;
1622 /*****************************************************************************
1624 *****************************************************************************/
1625 static void MMIDisplayReply( access_t *p_access, int i_session_id )
1627 uint8_t p_response[2];
1629 p_response[0] = DRI_MMI_MODE_ACK;
1630 p_response[1] = MM_HIGH_LEVEL;
1632 APDUSend( p_access, i_session_id, AOT_DISPLAY_REPLY, p_response, 2 );
1634 msg_Dbg( p_access, "sending DisplayReply on session (%d)", i_session_id );
1637 /*****************************************************************************
1639 *****************************************************************************/
1640 static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size )
1642 int i_tag = APDUGetTag( *pp_apdu, *pi_size );
1646 if ( i_tag != AOT_TEXT_LAST )
1648 msg_Err( p_access, "unexpected text tag: %06x", i_tag );
1650 return strdup( "" );
1653 d = APDUGetLength( *pp_apdu, &l );
1658 return dvbsi_to_utf8((char*)d,l);
1661 /*****************************************************************************
1663 *****************************************************************************/
1664 static void MMIHandleEnq( access_t *p_access, int i_session_id,
1665 uint8_t *p_apdu, int i_size )
1667 VLC_UNUSED( i_size );
1669 access_sys_t *p_sys = p_access->p_sys;
1670 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1671 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1673 uint8_t *d = APDUGetLength( p_apdu, &l );
1675 en50221_MMIFree( &p_mmi->last_object );
1676 p_mmi->last_object.i_object_type = EN50221_MMI_ENQ;
1677 p_mmi->last_object.u.enq.b_blind = (*d & 0x1) ? true : false;
1678 d += 2; /* skip answer_text_length because it is not mandatory */
1680 p_mmi->last_object.u.enq.psz_text = malloc( l + 1 );
1681 assert( p_mmi->last_object.u.enq.psz_text );
1682 strncpy( p_mmi->last_object.u.enq.psz_text, (char *)d, l );
1683 p_mmi->last_object.u.enq.psz_text[l] = '\0';
1685 msg_Dbg( p_access, "MMI enq: %s%s", p_mmi->last_object.u.enq.psz_text,
1686 p_mmi->last_object.u.enq.b_blind == true ? " (blind)" : "" );
1687 p_sys->pb_slot_mmi_expected[i_slot] = false;
1688 p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
1691 /*****************************************************************************
1693 *****************************************************************************/
1694 static void MMIHandleMenu( access_t *p_access, int i_session_id, int i_tag,
1695 uint8_t *p_apdu, int i_size )
1698 access_sys_t *p_sys = p_access->p_sys;
1699 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1700 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1702 uint8_t *d = APDUGetLength( p_apdu, &l );
1704 en50221_MMIFree( &p_mmi->last_object );
1705 p_mmi->last_object.i_object_type = (i_tag == AOT_MENU_LAST) ?
1706 EN50221_MMI_MENU : EN50221_MMI_LIST;
1707 p_mmi->last_object.u.menu.i_choices = 0;
1708 p_mmi->last_object.u.menu.ppsz_choices = NULL;
1712 l--; d++; /* choice_nb */
1714 #define GET_FIELD( x ) \
1717 p_mmi->last_object.u.menu.psz_##x \
1718 = MMIGetText( p_access, &d, &l ); \
1719 msg_Dbg( p_access, "MMI " STRINGIFY( x ) ": %s", \
1720 p_mmi->last_object.u.menu.psz_##x ); \
1724 GET_FIELD( subtitle );
1725 GET_FIELD( bottom );
1730 char *psz_text = MMIGetText( p_access, &d, &l );
1731 TAB_APPEND( p_mmi->last_object.u.menu.i_choices,
1732 p_mmi->last_object.u.menu.ppsz_choices,
1734 msg_Dbg( p_access, "MMI choice: %s", psz_text );
1737 p_sys->pb_slot_mmi_expected[i_slot] = false;
1738 p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
1741 /*****************************************************************************
1743 *****************************************************************************/
1744 static void MMIHandle( access_t *p_access, int i_session_id,
1745 uint8_t *p_apdu, int i_size )
1747 int i_tag = APDUGetTag( p_apdu, i_size );
1751 case AOT_DISPLAY_CONTROL:
1754 uint8_t *d = APDUGetLength( p_apdu, &l );
1760 case DCC_SET_MMI_MODE:
1761 if ( l == 2 && d[1] == MM_HIGH_LEVEL )
1762 MMIDisplayReply( p_access, i_session_id );
1764 msg_Err( p_access, "unsupported MMI mode %02x", d[1] );
1768 msg_Err( p_access, "unsupported display control command %02x",
1777 MMIHandleEnq( p_access, i_session_id, p_apdu, i_size );
1782 MMIHandleMenu( p_access, i_session_id, i_tag, p_apdu, i_size );
1786 SessionSendClose( p_access, i_session_id );
1790 msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
1794 /*****************************************************************************
1796 *****************************************************************************/
1797 static void MMIClose( access_t *p_access, int i_session_id )
1799 access_sys_t *p_sys = p_access->p_sys;
1800 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1801 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1803 en50221_MMIFree( &p_mmi->last_object );
1804 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1806 msg_Dbg( p_access, "closing MMI session (%d)", i_session_id );
1807 p_sys->pb_slot_mmi_expected[i_slot] = false;
1808 p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
1811 /*****************************************************************************
1813 *****************************************************************************/
1814 static void MMIOpen( access_t *p_access, int i_session_id )
1816 access_sys_t *p_sys = p_access->p_sys;
1819 msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
1821 p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
1822 p_sys->p_sessions[i_session_id - 1].pf_close = MMIClose;
1823 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(mmi_t));
1824 p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1826 p_mmi->last_object.i_object_type = EN50221_MMI_NONE;
1834 /*****************************************************************************
1835 * InitSlot: Open the transport layer
1836 *****************************************************************************/
1837 #define MAX_TC_RETRIES 20
1839 static int InitSlot( access_t * p_access, int i_slot )
1841 access_sys_t *p_sys = p_access->p_sys;
1844 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1847 msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
1849 return VLC_EGENERIC;
1852 /* This is out of the spec */
1853 for ( i = 0; i < MAX_TC_RETRIES; i++ )
1856 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
1857 && i_tag == T_CTC_REPLY )
1859 p_sys->pb_active_slot[i_slot] = true;
1863 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1867 "en50221_Init: couldn't send TPDU on slot %d",
1873 if ( p_sys->pb_active_slot[i_slot] )
1875 p_sys->i_ca_timeout = 100000;
1879 return VLC_EGENERIC;
1884 * External entry points
1887 /*****************************************************************************
1888 * en50221_Init : Initialize the CAM for en50221
1889 *****************************************************************************/
1890 int en50221_Init( access_t * p_access )
1892 access_sys_t *p_sys = p_access->p_sys;
1894 if( p_sys->i_ca_type & CA_CI_LINK )
1897 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1899 if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
1901 msg_Err( p_access, "en50221_Init: couldn't reset slot %d",
1906 p_sys->i_ca_timeout = 100000;
1907 /* Wait a bit otherwise it doesn't initialize properly... */
1914 struct ca_slot_info info;
1917 /* We don't reset the CAM in that case because it's done by the
1919 if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &info ) < 0 )
1921 msg_Err( p_access, "en50221_Init: couldn't get slot info" );
1922 close( p_sys->i_ca_handle );
1923 p_sys->i_ca_handle = 0;
1924 return VLC_EGENERIC;
1926 if( info.flags == 0 )
1928 msg_Err( p_access, "en50221_Init: no CAM inserted" );
1929 close( p_sys->i_ca_handle );
1930 p_sys->i_ca_handle = 0;
1931 return VLC_EGENERIC;
1934 /* Allocate a dummy sessions */
1935 p_sys->p_sessions[ 0 ].i_resource_id = RI_CONDITIONAL_ACCESS_SUPPORT;
1937 /* Get application info to find out which cam we are using and make
1938 sure everything is ready to play */
1941 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1942 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1943 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1944 memset( &ca_msg.msg[3], 0, 253 );
1945 APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1946 if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1948 msg_Err( p_access, "en50221_Init: failed getting message" );
1949 return VLC_EGENERIC;
1952 #if HLCI_WAIT_CAM_READY
1953 while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1955 if( !vlc_object_alive (p_access) ) return VLC_EGENERIC;
1957 msg_Dbg( p_access, "CAM: please wait" );
1958 APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1960 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1961 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1962 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1963 memset( &ca_msg.msg[3], 0, 253 );
1964 if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1966 msg_Err( p_access, "en50221_Init: failed getting message" );
1967 return VLC_EGENERIC;
1969 msg_Dbg( p_access, "en50221_Init: Got length: %d, tag: 0x%x", ca_msg.length, APDUGetTag( ca_msg.msg, ca_msg.length ) );
1972 if( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1974 msg_Err( p_access, "CAM returns garbage as application info!" );
1975 return VLC_EGENERIC;
1978 msg_Dbg( p_access, "found CAM %s using id 0x%x", &ca_msg.msg[12],
1979 (ca_msg.msg[8]<<8)|ca_msg.msg[9] );
1984 /*****************************************************************************
1985 * en50221_Poll : Poll the CAM for TPDUs
1986 *****************************************************************************/
1987 int en50221_Poll( access_t * p_access )
1989 access_sys_t *p_sys = p_access->p_sys;
1993 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1996 ca_slot_info_t sinfo;
1999 if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
2001 msg_Err( p_access, "en50221_Poll: couldn't get info on slot %d",
2006 if ( !(sinfo.flags & CA_CI_MODULE_READY) )
2008 if ( p_sys->pb_active_slot[i_slot] )
2010 msg_Dbg( p_access, "en50221_Poll: slot %d has been removed",
2012 p_sys->pb_active_slot[i_slot] = false;
2013 p_sys->pb_slot_mmi_expected[i_slot] = false;
2014 p_sys->pb_slot_mmi_undisplayed[i_slot] = false;
2016 /* Close all sessions for this slot. */
2017 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS;
2020 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2021 && p_sys->p_sessions[i_session_id - 1].i_slot
2024 if ( p_sys->p_sessions[i_session_id - 1].pf_close
2027 p_sys->p_sessions[i_session_id - 1].pf_close(
2028 p_access, i_session_id );
2030 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
2037 else if ( !p_sys->pb_active_slot[i_slot] )
2039 InitSlot( p_access, i_slot );
2041 if ( !p_sys->pb_active_slot[i_slot] )
2043 msg_Dbg( p_access, "en50221_Poll: resetting slot %d", i_slot );
2045 if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
2047 msg_Err( p_access, "en50221_Poll: couldn't reset slot %d",
2053 msg_Dbg( p_access, "en50221_Poll: slot %d is active",
2057 if ( !p_sys->pb_tc_has_data[i_slot] )
2059 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, NULL, 0 ) !=
2063 "en50221_Poll: couldn't send TPDU on slot %d",
2067 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) !=
2071 "en50221_Poll: couldn't recv TPDU on slot %d",
2077 while ( p_sys->pb_tc_has_data[i_slot] )
2079 uint8_t p_tpdu[MAX_TPDU_SIZE];
2080 int i_size, i_session_size;
2083 if ( TPDUSend( p_access, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
2086 "en50221_Poll: couldn't send TPDU on slot %d",
2090 if ( TPDURecv( p_access, i_slot, &i_tag, p_tpdu, &i_size ) !=
2094 "en50221_Poll: couldn't recv TPDU on slot %d",
2099 p_session = GetLength( &p_tpdu[3], &i_session_size );
2100 if ( i_session_size <= 1 )
2106 if ( i_tag != T_DATA_LAST )
2109 "en50221_Poll: fragmented TPDU not supported" );
2113 SPDUHandle( p_access, i_slot, p_session, i_session_size );
2117 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2119 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2120 && p_sys->p_sessions[i_session_id - 1].pf_manage )
2122 p_sys->p_sessions[i_session_id - 1].pf_manage( p_access,
2131 /*****************************************************************************
2132 * en50221_SetCAPMT :
2133 *****************************************************************************/
2134 int en50221_SetCAPMT( access_t * p_access, dvbpsi_pmt_t *p_pmt )
2136 access_sys_t *p_sys = p_access->p_sys;
2137 int i, i_session_id;
2138 bool b_update = false;
2139 bool b_needs_descrambling = CAPMTNeedsDescrambling( p_pmt );
2141 for ( i = 0; i < MAX_PROGRAMS; i++ )
2143 if ( p_sys->pp_selected_programs[i] != NULL
2144 && p_sys->pp_selected_programs[i]->i_program_number
2145 == p_pmt->i_program_number )
2149 if ( !b_needs_descrambling )
2151 dvbpsi_DeletePMT( p_pmt );
2152 p_pmt = p_sys->pp_selected_programs[i];
2153 p_sys->pp_selected_programs[i] = NULL;
2155 else if( p_pmt != p_sys->pp_selected_programs[i] )
2157 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
2158 p_sys->pp_selected_programs[i] = p_pmt;
2165 if ( !b_update && b_needs_descrambling )
2167 for ( i = 0; i < MAX_PROGRAMS; i++ )
2169 if ( p_sys->pp_selected_programs[i] == NULL )
2171 p_sys->pp_selected_programs[i] = p_pmt;
2177 if ( b_update || b_needs_descrambling )
2179 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2181 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2182 == RI_CONDITIONAL_ACCESS_SUPPORT )
2184 if ( b_update && b_needs_descrambling )
2185 CAPMTUpdate( p_access, i_session_id, p_pmt );
2186 else if ( b_update )
2187 CAPMTDelete( p_access, i_session_id, p_pmt );
2189 CAPMTAdd( p_access, i_session_id, p_pmt );
2194 if ( !b_needs_descrambling )
2196 dvbpsi_DeletePMT( p_pmt );
2202 /*****************************************************************************
2204 *****************************************************************************/
2205 int en50221_OpenMMI( access_t * p_access, int i_slot )
2207 access_sys_t *p_sys = p_access->p_sys;
2209 if( p_sys->i_ca_type & CA_CI_LINK )
2212 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2214 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2215 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2218 "MMI menu is already opened on slot %d (session=%d)",
2219 i_slot, i_session_id );
2224 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2226 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2227 == RI_APPLICATION_INFORMATION
2228 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2230 ApplicationInformationEnterMenu( p_access, i_session_id );
2235 msg_Err( p_access, "no application information on slot %d", i_slot );
2236 return VLC_EGENERIC;
2240 msg_Err( p_access, "MMI menu not supported" );
2241 return VLC_EGENERIC;
2245 /*****************************************************************************
2246 * en50221_CloseMMI :
2247 *****************************************************************************/
2248 int en50221_CloseMMI( access_t * p_access, int i_slot )
2250 access_sys_t *p_sys = p_access->p_sys;
2252 if( p_sys->i_ca_type & CA_CI_LINK )
2255 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2257 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2258 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2260 MMISendClose( p_access, i_session_id );
2265 msg_Warn( p_access, "closing a non-existing MMI session on slot %d",
2267 return VLC_EGENERIC;
2271 msg_Err( p_access, "MMI menu not supported" );
2272 return VLC_EGENERIC;
2276 /*****************************************************************************
2277 * en50221_GetMMIObject :
2278 *****************************************************************************/
2279 en50221_mmi_object_t *en50221_GetMMIObject( access_t * p_access,
2282 access_sys_t *p_sys = p_access->p_sys;
2285 if ( p_sys->pb_slot_mmi_expected[i_slot] == true )
2286 return NULL; /* should not happen */
2288 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2290 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2291 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2294 (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
2295 if ( p_mmi == NULL )
2296 return NULL; /* should not happen */
2297 return &p_mmi->last_object;
2305 /*****************************************************************************
2306 * en50221_SendMMIObject :
2307 *****************************************************************************/
2308 void en50221_SendMMIObject( access_t * p_access, int i_slot,
2309 en50221_mmi_object_t *p_object )
2311 access_sys_t *p_sys = p_access->p_sys;
2314 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2316 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2317 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2319 MMISendObject( p_access, i_session_id, p_object );
2324 msg_Err( p_access, "SendMMIObject when no MMI session is opened !" );
2327 /*****************************************************************************
2329 *****************************************************************************/
2330 void en50221_End( access_t * p_access )
2332 access_sys_t *p_sys = p_access->p_sys;
2333 int i_session_id, i;
2335 for ( i = 0; i < MAX_PROGRAMS; i++ )
2337 if ( p_sys->pp_selected_programs[i] != NULL )
2339 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
2343 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2345 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2346 && p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
2348 p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
2353 /* Leave the CAM configured, so that it can be reused in another
2357 static inline void *FixUTF8( char *p )
2363 char *dvbsi_to_utf8( const char *psz_instring, size_t i_length )
2365 const char *psz_encoding, *psz_stringstart;
2366 char *psz_outstring, *psz_tmp;
2367 char psz_encbuf[12];
2369 vlc_iconv_t iconv_handle;
2370 if( i_length < 1 ) return NULL;
2371 if( psz_instring[0] < 0 || psz_instring[0] >= 0x20 )
2373 psz_stringstart = psz_instring;
2374 psz_encoding = "ISO_8859-1"; /* should be ISO6937 according to spec, but this seems to be the one used */
2375 } else switch( psz_instring[0] )
2378 psz_stringstart = &psz_instring[1];
2379 psz_encoding = "ISO_8859-5";
2382 psz_stringstart = &psz_instring[1];
2383 psz_encoding = "ISO_8859-6";
2386 psz_stringstart = &psz_instring[1];
2387 psz_encoding = "ISO_8859-7";
2390 psz_stringstart = &psz_instring[1];
2391 psz_encoding = "ISO_8859-8";
2394 psz_stringstart = &psz_instring[1];
2395 psz_encoding = "ISO_8859-9";
2398 psz_stringstart = &psz_instring[1];
2399 psz_encoding = "ISO_8859-10";
2402 psz_stringstart = &psz_instring[1];
2403 psz_encoding = "ISO_8859-11";
2406 psz_stringstart = &psz_instring[1]; /*possibly reserved?*/
2407 psz_encoding = "ISO_8859-12";
2410 psz_stringstart = &psz_instring[1];
2411 psz_encoding = "ISO_8859-13";
2414 psz_stringstart = &psz_instring[1];
2415 psz_encoding = "ISO_8859-14";
2418 psz_stringstart = &psz_instring[1];
2419 psz_encoding = "ISO_8859-15";
2422 if( i_length < 3 || psz_instring[1] != '\0' || psz_instring[2] > 0x0f
2423 || psz_instring[2] == 0 )
2424 return FixUTF8(strndup(psz_instring,i_length));
2425 sprintf( psz_encbuf, "ISO_8859-%d", psz_instring[2] );
2426 psz_stringstart = &psz_instring[3];
2427 psz_encoding = psz_encbuf;
2430 psz_stringstart = &psz_instring[1];
2431 psz_encoding = "UTF-16";
2434 psz_stringstart = &psz_instring[1];
2435 psz_encoding = "KSC5601-1987";
2438 psz_stringstart = &psz_instring[1];
2439 psz_encoding = "GB2312";/*GB-2312-1980 */
2442 psz_stringstart = &psz_instring[1];
2443 psz_encoding = "BIG-5";
2446 return FixUTF8(strndup(&psz_instring[1],i_length-1));
2450 return FixUTF8(strndup(psz_instring,i_length));
2452 iconv_handle = vlc_iconv_open( "UTF-8", psz_encoding );
2453 i_in = i_length - (psz_stringstart - psz_instring );
2455 psz_outstring = psz_tmp = (char*)malloc( i_out + 1 );
2456 assert( psz_outstring );
2457 vlc_iconv( iconv_handle, &psz_stringstart, &i_in, &psz_tmp, &i_out );
2458 vlc_iconv_close( iconv_handle );
2460 return psz_outstring;