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>
60 # include "descriptor.h"
61 # include "tables/pat.h"
62 # include "tables/pmt.h"
63 # include "descriptors/dr.h"
68 # include "vlc_httpd.h"
73 #include <vlc_charset.h>
76 #define HLCI_WAIT_CAM_READY 0
77 #define CAM_PROG_MAX MAX_PROGRAMS
79 static void ResourceManagerOpen( access_t * p_access, int i_session_id );
80 static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
81 static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
82 static void DateTimeOpen( access_t * p_access, int i_session_id );
83 static void MMIOpen( access_t * p_access, int i_session_id );
84 static char *dvbsi_to_utf8( char *psz_instring, size_t i_length );
86 /*****************************************************************************
88 *****************************************************************************/
89 #define SIZE_INDICATOR 0x80
91 static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
93 *pi_length = *p_data++;
95 if ( (*pi_length & SIZE_INDICATOR) != 0 )
97 int l = *pi_length & ~SIZE_INDICATOR;
101 for ( i = 0; i < l; i++ )
102 *pi_length = (*pi_length << 8) | *p_data++;
108 static uint8_t *SetLength( uint8_t *p_data, int i_length )
112 if ( i_length < 128 )
116 else if ( i_length < 256 )
118 *p++ = SIZE_INDICATOR | 0x1;
121 else if ( i_length < 65536 )
123 *p++ = SIZE_INDICATOR | 0x2;
124 *p++ = i_length >> 8;
125 *p++ = i_length & 0xff;
127 else if ( i_length < 16777216 )
129 *p++ = SIZE_INDICATOR | 0x3;
130 *p++ = i_length >> 16;
131 *p++ = (i_length >> 8) & 0xff;
132 *p++ = i_length & 0xff;
136 *p++ = SIZE_INDICATOR | 0x4;
137 *p++ = i_length >> 24;
138 *p++ = (i_length >> 16) & 0xff;
139 *p++ = (i_length >> 8) & 0xff;
140 *p++ = i_length & 0xff;
151 #define MAX_TPDU_SIZE 2048
152 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
154 #define DATA_INDICATOR 0x80
158 #define T_CREATE_TC 0x82
159 #define T_CTC_REPLY 0x83
160 #define T_DELETE_TC 0x84
161 #define T_DTC_REPLY 0x85
162 #define T_REQUEST_TC 0x86
163 #define T_NEW_TC 0x87
164 #define T_TC_ERROR 0x88
165 #define T_DATA_LAST 0xA0
166 #define T_DATA_MORE 0xA1
168 static void Dump( bool b_outgoing, uint8_t *p_data, int i_size )
173 fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
174 for ( i = 0; i < i_size && i < MAX_DUMP; i++)
175 fprintf(stderr, "%02X ", p_data[i]);
176 fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
180 /*****************************************************************************
182 *****************************************************************************/
183 static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
184 const uint8_t *p_content, int i_length )
186 access_sys_t *p_sys = p_access->p_sys;
187 uint8_t i_tcid = i_slot + 1;
188 uint8_t p_data[MAX_TPDU_SIZE];
204 p_data[3] = 1; /* length */
211 p_data[3] = 2; /* length */
213 p_data[5] = p_content[0];
220 /* i_length <= MAX_TPDU_DATA */
221 uint8_t *p = p_data + 3;
222 p = SetLength( p, i_length + 1 );
226 memcpy( p, p_content, i_length );
227 i_size = i_length + (p - p_data);
234 Dump( true, p_data, i_size );
236 if ( write( p_sys->i_ca_handle, p_data, i_size ) != i_size )
238 msg_Err( p_access, "cannot write to CAM device (%m)" );
246 /*****************************************************************************
248 *****************************************************************************/
249 #define CAM_READ_TIMEOUT 3500 // ms
251 static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
252 uint8_t *p_data, int *pi_size )
254 access_sys_t *p_sys = p_access->p_sys;
255 uint8_t i_tcid = i_slot + 1;
257 struct pollfd pfd[1];
259 pfd[0].fd = p_sys->i_ca_handle;
260 pfd[0].events = POLLIN;
261 if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
263 msg_Err( p_access, "cannot poll from CAM device" );
267 if ( pi_size == NULL )
269 p_data = malloc( MAX_TPDU_SIZE );
274 i_size = read( p_sys->i_ca_handle, p_data, MAX_TPDU_SIZE );
276 if ( i_size >= 0 || errno != EINTR )
282 msg_Err( p_access, "cannot read from CAM device (%d:%m)", i_size );
286 if ( p_data[1] != i_tcid )
288 msg_Err( p_access, "invalid read from CAM device (%d instead of %d)",
294 p_sys->pb_tc_has_data[i_slot] = (i_size >= 4
295 && p_data[i_size - 4] == T_SB
296 && p_data[i_size - 3] == 2
297 && (p_data[i_size - 1] & DATA_INDICATOR))
300 Dump( false, p_data, i_size );
302 if ( pi_size == NULL )
315 #define ST_SESSION_NUMBER 0x90
316 #define ST_OPEN_SESSION_REQUEST 0x91
317 #define ST_OPEN_SESSION_RESPONSE 0x92
318 #define ST_CREATE_SESSION 0x93
319 #define ST_CREATE_SESSION_RESPONSE 0x94
320 #define ST_CLOSE_SESSION_REQUEST 0x95
321 #define ST_CLOSE_SESSION_RESPONSE 0x96
324 #define SS_NOT_ALLOCATED 0xF0
326 #define RI_RESOURCE_MANAGER 0x00010041
327 #define RI_APPLICATION_INFORMATION 0x00020041
328 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
329 #define RI_HOST_CONTROL 0x00200041
330 #define RI_DATE_TIME 0x00240041
331 #define RI_MMI 0x00400041
333 static int ResourceIdToInt( uint8_t *p_data )
335 return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
336 | ((int)p_data[2] << 8) | p_data[3];
339 /*****************************************************************************
341 *****************************************************************************/
342 static int SPDUSend( access_t * p_access, int i_session_id,
343 uint8_t *p_data, int i_size )
345 access_sys_t *p_sys = p_access->p_sys;
346 uint8_t *p_spdu = malloc( i_size + 4 );
349 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
351 *p++ = ST_SESSION_NUMBER;
353 *p++ = (i_session_id >> 8);
354 *p++ = i_session_id & 0xff;
356 memcpy( p, p_data, i_size );
363 if ( i_size > MAX_TPDU_DATA )
365 if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
366 MAX_TPDU_DATA ) != VLC_SUCCESS )
368 msg_Err( p_access, "couldn't send TPDU on session %d",
374 i_size -= MAX_TPDU_DATA;
378 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
381 msg_Err( p_access, "couldn't send TPDU on session %d",
389 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
392 msg_Err( p_access, "couldn't recv TPDU on session %d",
403 /*****************************************************************************
405 *****************************************************************************/
406 static void SessionOpen( access_t * p_access, uint8_t i_slot,
407 uint8_t *p_spdu, int i_size )
409 access_sys_t *p_sys = p_access->p_sys;
411 int i_resource_id = ResourceIdToInt( &p_spdu[2] );
412 uint8_t p_response[16];
413 int i_status = SS_NOT_ALLOCATED;
416 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
418 if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
421 if ( i_session_id == MAX_SESSIONS )
423 msg_Err( p_access, "too many sessions !" );
426 p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
427 p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
428 p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
429 p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
431 if ( i_resource_id == RI_RESOURCE_MANAGER
432 || i_resource_id == RI_APPLICATION_INFORMATION
433 || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
434 || i_resource_id == RI_DATE_TIME
435 || i_resource_id == RI_MMI )
440 p_response[0] = ST_OPEN_SESSION_RESPONSE;
442 p_response[2] = i_status;
443 p_response[3] = p_spdu[2];
444 p_response[4] = p_spdu[3];
445 p_response[5] = p_spdu[4];
446 p_response[6] = p_spdu[5];
447 p_response[7] = i_session_id >> 8;
448 p_response[8] = i_session_id & 0xff;
450 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) !=
454 "SessionOpen: couldn't send TPDU on slot %d", i_slot );
457 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
460 "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
464 switch ( i_resource_id )
466 case RI_RESOURCE_MANAGER:
467 ResourceManagerOpen( p_access, i_session_id ); break;
468 case RI_APPLICATION_INFORMATION:
469 ApplicationInformationOpen( p_access, i_session_id ); break;
470 case RI_CONDITIONAL_ACCESS_SUPPORT:
471 ConditionalAccessOpen( p_access, i_session_id ); break;
473 DateTimeOpen( p_access, i_session_id ); break;
475 MMIOpen( p_access, i_session_id ); break;
477 case RI_HOST_CONTROL:
479 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
480 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
485 /* unused code for the moment - commented out to keep gcc happy */
486 /*****************************************************************************
488 *****************************************************************************/
489 static void SessionCreate( access_t * p_access, int i_slot, int i_resource_id )
491 access_sys_t *p_sys = p_access->p_sys;
492 uint8_t p_response[16];
496 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
498 if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
501 if ( i_session_id == MAX_SESSIONS )
503 msg_Err( p_access, "too many sessions !" );
506 p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
507 p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
508 p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
509 p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
510 p_sys->p_sessions[i_session_id - 1].p_sys = NULL;
512 p_response[0] = ST_CREATE_SESSION;
514 p_response[2] = i_resource_id >> 24;
515 p_response[3] = (i_resource_id >> 16) & 0xff;
516 p_response[4] = (i_resource_id >> 8) & 0xff;
517 p_response[5] = i_resource_id & 0xff;
518 p_response[6] = i_session_id >> 8;
519 p_response[7] = i_session_id & 0xff;
521 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
525 "SessionCreate: couldn't send TPDU on slot %d", i_slot );
528 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
531 "SessionCreate: couldn't recv TPDU on slot %d", i_slot );
537 /*****************************************************************************
538 * SessionCreateResponse
539 *****************************************************************************/
540 static void SessionCreateResponse( access_t * p_access, uint8_t i_slot,
541 uint8_t *p_spdu, int i_size )
543 access_sys_t *p_sys = p_access->p_sys;
544 int i_status = p_spdu[2];
545 int i_resource_id = ResourceIdToInt( &p_spdu[3] );
546 int i_session_id = ((int)p_spdu[7] << 8) | p_spdu[8];
548 if ( i_status != SS_OK )
550 msg_Err( p_access, "SessionCreateResponse: failed to open session %d"
551 " resource=0x%x status=0x%x", i_session_id, i_resource_id,
553 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
557 switch ( i_resource_id )
559 case RI_RESOURCE_MANAGER:
560 ResourceManagerOpen( p_access, i_session_id ); break;
561 case RI_APPLICATION_INFORMATION:
562 ApplicationInformationOpen( p_access, i_session_id ); break;
563 case RI_CONDITIONAL_ACCESS_SUPPORT:
564 ConditionalAccessOpen( p_access, i_session_id ); break;
566 DateTimeOpen( p_access, i_session_id ); break;
568 MMIOpen( p_access, i_session_id ); break;
570 case RI_HOST_CONTROL:
572 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
573 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
577 /*****************************************************************************
579 *****************************************************************************/
580 static void SessionSendClose( access_t * p_access, int i_session_id )
582 access_sys_t *p_sys = p_access->p_sys;
583 uint8_t p_response[16];
585 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
587 p_response[0] = ST_CLOSE_SESSION_REQUEST;
589 p_response[2] = i_session_id >> 8;
590 p_response[3] = i_session_id & 0xff;
592 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
596 "SessionSendClose: couldn't send TPDU on slot %d", i_slot );
599 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
602 "SessionSendClose: couldn't recv TPDU on slot %d", i_slot );
607 /*****************************************************************************
609 *****************************************************************************/
610 static void SessionClose( access_t * p_access, int i_session_id )
612 access_sys_t *p_sys = p_access->p_sys;
613 uint8_t p_response[16];
615 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
617 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
618 p_sys->p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
619 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
621 p_response[0] = ST_CLOSE_SESSION_RESPONSE;
623 p_response[2] = SS_OK;
624 p_response[3] = i_session_id >> 8;
625 p_response[4] = i_session_id & 0xff;
627 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
631 "SessionClose: couldn't send TPDU on slot %d", i_slot );
634 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
637 "SessionClose: couldn't recv TPDU on slot %d", i_slot );
642 /*****************************************************************************
644 *****************************************************************************/
645 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
646 uint8_t *p_spdu, int i_size )
648 access_sys_t *p_sys = p_access->p_sys;
653 case ST_SESSION_NUMBER:
656 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
657 p_sys->p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
658 p_spdu + 4, i_size - 4 );
661 case ST_OPEN_SESSION_REQUEST:
662 if ( i_size != 6 || p_spdu[1] != 0x4 )
664 SessionOpen( p_access, i_slot, p_spdu, i_size );
667 case ST_CREATE_SESSION_RESPONSE:
668 if ( i_size != 9 || p_spdu[1] != 0x7 )
670 SessionCreateResponse( p_access, i_slot, p_spdu, i_size );
673 case ST_CLOSE_SESSION_REQUEST:
674 if ( i_size != 4 || p_spdu[1] != 0x2 )
676 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
677 SessionClose( p_access, i_session_id );
680 case ST_CLOSE_SESSION_RESPONSE:
681 if ( i_size != 5 || p_spdu[1] != 0x3 )
683 i_session_id = ((int)p_spdu[3] << 8) | p_spdu[4];
686 msg_Err( p_access, "closing a session which is not allocated (%d)",
691 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
692 p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
694 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
699 msg_Err( p_access, "unexpected tag in SPDUHandle (%x)", p_spdu[0] );
709 #define AOT_NONE 0x000000
710 #define AOT_PROFILE_ENQ 0x9F8010
711 #define AOT_PROFILE 0x9F8011
712 #define AOT_PROFILE_CHANGE 0x9F8012
713 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
714 #define AOT_APPLICATION_INFO 0x9F8021
715 #define AOT_ENTER_MENU 0x9F8022
716 #define AOT_CA_INFO_ENQ 0x9F8030
717 #define AOT_CA_INFO 0x9F8031
718 #define AOT_CA_PMT 0x9F8032
719 #define AOT_CA_PMT_REPLY 0x9F8033
720 #define AOT_TUNE 0x9F8400
721 #define AOT_REPLACE 0x9F8401
722 #define AOT_CLEAR_REPLACE 0x9F8402
723 #define AOT_ASK_RELEASE 0x9F8403
724 #define AOT_DATE_TIME_ENQ 0x9F8440
725 #define AOT_DATE_TIME 0x9F8441
726 #define AOT_CLOSE_MMI 0x9F8800
727 #define AOT_DISPLAY_CONTROL 0x9F8801
728 #define AOT_DISPLAY_REPLY 0x9F8802
729 #define AOT_TEXT_LAST 0x9F8803
730 #define AOT_TEXT_MORE 0x9F8804
731 #define AOT_KEYPAD_CONTROL 0x9F8805
732 #define AOT_KEYPRESS 0x9F8806
733 #define AOT_ENQ 0x9F8807
734 #define AOT_ANSW 0x9F8808
735 #define AOT_MENU_LAST 0x9F8809
736 #define AOT_MENU_MORE 0x9F880A
737 #define AOT_MENU_ANSW 0x9F880B
738 #define AOT_LIST_LAST 0x9F880C
739 #define AOT_LIST_MORE 0x9F880D
740 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
741 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
742 #define AOT_DISPLAY_MESSAGE 0x9F8810
743 #define AOT_SCENE_END_MARK 0x9F8811
744 #define AOT_SCENE_DONE 0x9F8812
745 #define AOT_SCENE_CONTROL 0x9F8813
746 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
747 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
748 #define AOT_FLUSH_DOWNLOAD 0x9F8816
749 #define AOT_DOWNLOAD_REPLY 0x9F8817
750 #define AOT_COMMS_CMD 0x9F8C00
751 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
752 #define AOT_COMMS_REPLY 0x9F8C02
753 #define AOT_COMMS_SEND_LAST 0x9F8C03
754 #define AOT_COMMS_SEND_MORE 0x9F8C04
755 #define AOT_COMMS_RCV_LAST 0x9F8C05
756 #define AOT_COMMS_RCV_MORE 0x9F8C06
758 /*****************************************************************************
760 *****************************************************************************/
761 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
766 for ( i = 0; i < 3; i++ )
767 t = (t << 8) | *p_apdu++;
774 /*****************************************************************************
776 *****************************************************************************/
777 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
779 return GetLength( &p_apdu[3], pi_size );
782 /*****************************************************************************
784 *****************************************************************************/
785 static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
786 uint8_t *p_data, int i_size )
788 access_sys_t *p_sys = p_access->p_sys;
789 uint8_t *p_apdu = malloc( i_size + 12 );
794 *p++ = (i_tag >> 16);
795 *p++ = (i_tag >> 8) & 0xff;
797 p = SetLength( p, i_size );
799 memcpy( p, p_data, i_size );
800 if ( p_sys->i_ca_type == CA_CI_LINK )
802 i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
806 if ( i_size + p - p_apdu > 256 )
808 msg_Err( p_access, "CAM: apdu overflow" );
809 i_ret = VLC_EGENERIC;
814 ca_msg.length = i_size + p - p_apdu;
815 if ( i_size == 0 ) ca_msg.length=3;
816 psz_hex = (char*)malloc( ca_msg.length*3 + 1);
817 memcpy( ca_msg.msg, p_apdu, i_size + p - p_apdu );
818 i_ret = ioctl(p_sys->i_ca_handle, CA_SEND_MSG, &ca_msg );
821 msg_Err( p_access, "Error sending to CAM: %m" );
822 i_ret = VLC_EGENERIC;
834 /*****************************************************************************
835 * ResourceManagerHandle
836 *****************************************************************************/
837 static void ResourceManagerHandle( access_t * p_access, int i_session_id,
838 uint8_t *p_apdu, int i_size )
840 int i_tag = APDUGetTag( p_apdu, i_size );
844 case AOT_PROFILE_ENQ:
846 int resources[] = { htonl(RI_RESOURCE_MANAGER),
847 htonl(RI_APPLICATION_INFORMATION),
848 htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
852 APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
857 APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
861 msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
866 /*****************************************************************************
867 * ResourceManagerOpen
868 *****************************************************************************/
869 static void ResourceManagerOpen( access_t * p_access, int i_session_id )
871 access_sys_t *p_sys = p_access->p_sys;
873 msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
875 p_sys->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
877 APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
881 * Application Information
884 /*****************************************************************************
885 * ApplicationInformationEnterMenu
886 *****************************************************************************/
887 static void ApplicationInformationEnterMenu( access_t * p_access,
890 access_sys_t *p_sys = p_access->p_sys;
891 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
893 msg_Dbg( p_access, "entering MMI menus on session %d", i_session_id );
894 APDUSend( p_access, i_session_id, AOT_ENTER_MENU, NULL, 0 );
895 p_sys->pb_slot_mmi_expected[i_slot] = true;
898 /*****************************************************************************
899 * ApplicationInformationHandle
900 *****************************************************************************/
901 static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
902 uint8_t *p_apdu, int i_size )
904 int i_tag = APDUGetTag( p_apdu, i_size );
908 case AOT_APPLICATION_INFO:
910 int i_type, i_manufacturer, i_code;
912 uint8_t *d = APDUGetLength( p_apdu, &l );
915 p_apdu[l + 4] = '\0';
918 i_manufacturer = ((int)d[0] << 8) | d[1];
920 i_code = ((int)d[0] << 8) | d[1];
922 d = GetLength( d, &l );
924 msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
925 d, i_type, i_manufacturer, i_code );
930 "unexpected tag in ApplicationInformationHandle (0x%x)",
935 /*****************************************************************************
936 * ApplicationInformationOpen
937 *****************************************************************************/
938 static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
940 access_sys_t *p_sys = p_access->p_sys;
942 msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
944 p_sys->p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
946 APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
953 #define MAX_CASYSTEM_IDS 16
957 uint16_t pi_system_ids[MAX_CASYSTEM_IDS + 1];
960 static bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
963 if( !p_ids ) return true;
965 while ( p_ids->pi_system_ids[i] )
967 if ( p_ids->pi_system_ids[i] == i_id )
975 /*****************************************************************************
976 * CAPMTNeedsDescrambling
977 *****************************************************************************/
978 static bool CAPMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
980 dvbpsi_descriptor_t *p_dr;
981 dvbpsi_pmt_es_t *p_es;
983 for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
985 if( p_dr->i_tag == 0x9 )
991 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
993 for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
994 p_dr = p_dr->p_next )
996 if( p_dr->i_tag == 0x9 )
1006 /*****************************************************************************
1008 *****************************************************************************/
1009 static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr )
1013 while ( p_dr != NULL )
1015 if( p_dr->i_tag == 0x9 )
1017 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1019 if ( CheckSystemID( p_ids, i_sysid ) )
1020 i_cad_size += p_dr->i_length + 2;
1022 p_dr = p_dr->p_next;
1028 static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt,
1029 uint16_t i_program_number, uint8_t i_version,
1030 int i_size, dvbpsi_descriptor_t *p_dr,
1036 p_data = malloc( 7 + i_size );
1038 p_data = malloc( 6 );
1040 p_data[0] = i_list_mgt;
1041 p_data[1] = i_program_number >> 8;
1042 p_data[2] = i_program_number & 0xff;
1043 p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
1049 p_data[4] = (i_size + 1) >> 8;
1050 p_data[5] = (i_size + 1) & 0xff;
1054 while ( p_dr != NULL )
1056 if( p_dr->i_tag == 0x9 )
1058 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1060 if ( CheckSystemID( p_ids, i_sysid ) )
1063 p_data[i + 1] = p_dr->i_length;
1064 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1065 // p_data[i+4] &= 0x1f;
1066 i += p_dr->i_length + 2;
1069 p_dr = p_dr->p_next;
1081 static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt,
1082 int i_capmt_size, uint8_t i_type, uint16_t i_pid,
1083 int i_size, dvbpsi_descriptor_t *p_dr,
1090 p_data = realloc( p_capmt, i_capmt_size + 6 + i_size );
1092 p_data = realloc( p_capmt, i_capmt_size + 5 );
1097 p_data[i + 1] = i_pid >> 8;
1098 p_data[i + 2] = i_pid & 0xff;
1102 p_data[i + 3] = (i_size + 1) >> 8;
1103 p_data[i + 4] = (i_size + 1) & 0xff;
1104 p_data[i + 5] = i_cmd;
1107 while ( p_dr != NULL )
1109 if( p_dr->i_tag == 0x9 )
1111 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1113 if ( CheckSystemID( p_ids, i_sysid ) )
1116 p_data[i + 1] = p_dr->i_length;
1117 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1118 i += p_dr->i_length + 2;
1121 p_dr = p_dr->p_next;
1133 static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id,
1134 dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt,
1135 uint8_t i_cmd, int *pi_capmt_size )
1137 access_sys_t *p_sys = p_access->p_sys;
1138 system_ids_t *p_ids =
1139 (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1140 dvbpsi_pmt_es_t *p_es;
1141 int i_cad_size, i_cad_program_size;
1144 i_cad_size = i_cad_program_size =
1145 GetCADSize( p_ids, p_pmt->p_first_descriptor );
1146 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1148 i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor );
1154 "no compatible scrambling system for SID %d on session %d",
1155 p_pmt->i_program_number, i_session_id );
1160 p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number,
1161 p_pmt->i_version, i_cad_program_size,
1162 p_pmt->p_first_descriptor, i_cmd );
1164 if ( i_cad_program_size )
1165 *pi_capmt_size = 7 + i_cad_program_size;
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 );
1173 if ( i_cad_size || i_cad_program_size )
1175 p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type,
1176 p_es->i_pid, i_cad_size,
1177 p_es->p_first_descriptor, i_cmd );
1179 *pi_capmt_size += 6 + i_cad_size;
1181 *pi_capmt_size += 5;
1188 /*****************************************************************************
1190 *****************************************************************************/
1191 static void CAPMTFirst( access_t * p_access, int i_session_id,
1192 dvbpsi_pmt_t *p_pmt )
1197 msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d",
1198 p_pmt->i_program_number, i_session_id );
1200 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1201 0x3 /* only */, 0x1 /* ok_descrambling */,
1205 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1208 /*****************************************************************************
1210 *****************************************************************************/
1211 static void CAPMTAdd( access_t * p_access, int i_session_id,
1212 dvbpsi_pmt_t *p_pmt )
1217 if( p_access->p_sys->i_selected_programs >= CAM_PROG_MAX )
1219 msg_Warn( p_access, "Not adding CAPMT for SID %d, too many programs",
1220 p_pmt->i_program_number );
1223 p_access->p_sys->i_selected_programs++;
1224 if( p_access->p_sys->i_selected_programs == 1 )
1226 CAPMTFirst( p_access, i_session_id, p_pmt );
1231 msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
1232 p_pmt->i_program_number, i_session_id );
1234 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1235 0x4 /* add */, 0x1 /* ok_descrambling */,
1239 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1242 /*****************************************************************************
1244 *****************************************************************************/
1245 static void CAPMTUpdate( access_t * p_access, int i_session_id,
1246 dvbpsi_pmt_t *p_pmt )
1251 msg_Dbg( p_access, "updating CAPMT for SID %d on session %d",
1252 p_pmt->i_program_number, i_session_id );
1254 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1255 0x5 /* update */, 0x1 /* ok_descrambling */,
1259 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1262 /*****************************************************************************
1264 *****************************************************************************/
1265 static void CAPMTDelete( access_t * p_access, int i_session_id,
1266 dvbpsi_pmt_t *p_pmt )
1271 p_access->p_sys->i_selected_programs--;
1272 msg_Dbg( p_access, "deleting 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 */, 0x4 /* not selected */,
1280 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1283 /*****************************************************************************
1284 * ConditionalAccessHandle
1285 *****************************************************************************/
1286 static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
1287 uint8_t *p_apdu, int i_size )
1289 access_sys_t *p_sys = p_access->p_sys;
1290 system_ids_t *p_ids =
1291 (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1292 int i_tag = APDUGetTag( p_apdu, i_size );
1300 uint8_t *d = APDUGetLength( p_apdu, &l );
1301 msg_Dbg( p_access, "CA system IDs supported by the application :" );
1303 for ( i = 0; i < l / 2; i++ )
1305 p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
1307 msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] );
1309 p_ids->pi_system_ids[i] = 0;
1311 for ( i = 0; i < MAX_PROGRAMS; i++ )
1313 if ( p_sys->pp_selected_programs[i] != NULL )
1315 CAPMTAdd( p_access, i_session_id,
1316 p_sys->pp_selected_programs[i] );
1324 "unexpected tag in ConditionalAccessHandle (0x%x)",
1329 /*****************************************************************************
1330 * ConditionalAccessClose
1331 *****************************************************************************/
1332 static void ConditionalAccessClose( access_t * p_access, int i_session_id )
1334 access_sys_t *p_sys = p_access->p_sys;
1336 msg_Dbg( p_access, "closing ConditionalAccess session (%d)", i_session_id );
1338 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1341 /*****************************************************************************
1342 * ConditionalAccessOpen
1343 *****************************************************************************/
1344 static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
1346 access_sys_t *p_sys = p_access->p_sys;
1348 msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
1350 p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
1351 p_sys->p_sessions[i_session_id - 1].pf_close = ConditionalAccessClose;
1352 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(system_ids_t));
1353 memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0,
1354 sizeof(system_ids_t) );
1356 APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
1369 /*****************************************************************************
1371 *****************************************************************************/
1372 static void DateTimeSend( access_t * p_access, int i_session_id )
1374 access_sys_t *p_sys = p_access->p_sys;
1375 date_time_t *p_date =
1376 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1378 time_t t = time(NULL);
1382 if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
1384 int Y = tm_gmt.tm_year;
1385 int M = tm_gmt.tm_mon + 1;
1386 int D = tm_gmt.tm_mday;
1387 int L = (M == 1 || M == 2) ? 1 : 0;
1388 int MJD = 14956 + D + (int)((Y - L) * 365.25)
1389 + (int)((M + 1 + L * 12) * 30.6001);
1390 uint8_t p_response[7];
1392 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
1394 p_response[0] = htons(MJD) >> 8;
1395 p_response[1] = htons(MJD) & 0xff;
1396 p_response[2] = DEC2BCD(tm_gmt.tm_hour);
1397 p_response[3] = DEC2BCD(tm_gmt.tm_min);
1398 p_response[4] = DEC2BCD(tm_gmt.tm_sec);
1399 p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
1400 p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
1402 APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
1404 p_date->i_last = mdate();
1408 /*****************************************************************************
1410 *****************************************************************************/
1411 static void DateTimeHandle( access_t * p_access, int i_session_id,
1412 uint8_t *p_apdu, int i_size )
1414 access_sys_t *p_sys = p_access->p_sys;
1415 date_time_t *p_date =
1416 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1418 int i_tag = APDUGetTag( p_apdu, i_size );
1422 case AOT_DATE_TIME_ENQ:
1425 const uint8_t *d = APDUGetLength( p_apdu, &l );
1429 p_date->i_interval = *d;
1430 msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
1431 p_date->i_interval );
1434 p_date->i_interval = 0;
1436 DateTimeSend( p_access, i_session_id );
1440 msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
1444 /*****************************************************************************
1446 *****************************************************************************/
1447 static void DateTimeManage( access_t * p_access, int i_session_id )
1449 access_sys_t *p_sys = p_access->p_sys;
1450 date_time_t *p_date =
1451 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1453 if ( p_date->i_interval
1454 && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
1456 DateTimeSend( p_access, i_session_id );
1460 /*****************************************************************************
1462 *****************************************************************************/
1463 static void DateTimeClose( access_t * p_access, int i_session_id )
1465 access_sys_t *p_sys = p_access->p_sys;
1467 msg_Dbg( p_access, "closing DateTime session (%d)", i_session_id );
1469 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1472 /*****************************************************************************
1474 *****************************************************************************/
1475 static void DateTimeOpen( access_t * p_access, int i_session_id )
1477 access_sys_t *p_sys = p_access->p_sys;
1479 msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
1481 p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
1482 p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
1483 p_sys->p_sessions[i_session_id - 1].pf_close = DateTimeClose;
1484 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t));
1485 memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
1487 DateTimeSend( p_access, i_session_id );
1494 /* Display Control Commands */
1496 #define DCC_SET_MMI_MODE 0x01
1497 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
1498 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
1499 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
1500 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
1504 #define MM_HIGH_LEVEL 0x01
1505 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
1506 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
1508 /* Display Reply IDs */
1510 #define DRI_MMI_MODE_ACK 0x01
1511 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
1512 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
1513 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
1514 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
1515 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
1516 #define DRI_UNKNOWN_MMI_MODE 0xF1
1517 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
1521 #define EF_BLIND 0x01
1525 #define AI_CANCEL 0x00
1526 #define AI_ANSWER 0x01
1530 en50221_mmi_object_t last_object;
1533 /*****************************************************************************
1535 *****************************************************************************/
1536 static void MMISendObject( access_t *p_access, int i_session_id,
1537 en50221_mmi_object_t *p_object )
1539 access_sys_t *p_sys = p_access->p_sys;
1540 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1544 switch ( p_object->i_object_type )
1546 case EN50221_MMI_ANSW:
1548 i_size = 1 + strlen( p_object->u.answ.psz_answ );
1549 p_data = malloc( i_size );
1550 p_data[0] = (p_object->u.answ.b_ok == true) ? 0x1 : 0x0;
1551 strncpy( (char *)&p_data[1], p_object->u.answ.psz_answ, i_size - 1 );
1554 case EN50221_MMI_MENU_ANSW:
1555 i_tag = AOT_MENU_ANSW;
1557 p_data = malloc( i_size );
1558 p_data[0] = p_object->u.menu_answ.i_choice;
1562 msg_Err( p_access, "unknown MMI object %d", p_object->i_object_type );
1566 APDUSend( p_access, i_session_id, i_tag, p_data, i_size );
1569 p_sys->pb_slot_mmi_expected[i_slot] = true;
1572 /*****************************************************************************
1574 *****************************************************************************/
1575 static void MMISendClose( access_t *p_access, int i_session_id )
1577 access_sys_t *p_sys = p_access->p_sys;
1578 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1580 APDUSend( p_access, i_session_id, AOT_CLOSE_MMI, NULL, 0 );
1582 p_sys->pb_slot_mmi_expected[i_slot] = true;
1585 /*****************************************************************************
1587 *****************************************************************************/
1588 static void MMIDisplayReply( access_t *p_access, int i_session_id )
1590 uint8_t p_response[2];
1592 p_response[0] = DRI_MMI_MODE_ACK;
1593 p_response[1] = MM_HIGH_LEVEL;
1595 APDUSend( p_access, i_session_id, AOT_DISPLAY_REPLY, p_response, 2 );
1597 msg_Dbg( p_access, "sending DisplayReply on session (%d)", i_session_id );
1600 /*****************************************************************************
1602 *****************************************************************************/
1603 static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size )
1605 int i_tag = APDUGetTag( *pp_apdu, *pi_size );
1609 if ( i_tag != AOT_TEXT_LAST )
1611 msg_Err( p_access, "unexpected text tag: %06x", i_tag );
1613 return strdup( "" );
1616 d = APDUGetLength( *pp_apdu, &l );
1621 return dvbsi_to_utf8((char*)d,l);
1624 /*****************************************************************************
1626 *****************************************************************************/
1627 static void MMIHandleEnq( access_t *p_access, int i_session_id,
1628 uint8_t *p_apdu, int i_size )
1630 access_sys_t *p_sys = p_access->p_sys;
1631 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1632 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1634 uint8_t *d = APDUGetLength( p_apdu, &l );
1636 en50221_MMIFree( &p_mmi->last_object );
1637 p_mmi->last_object.i_object_type = EN50221_MMI_ENQ;
1638 p_mmi->last_object.u.enq.b_blind = (*d & 0x1) ? true : false;
1639 d += 2; /* skip answer_text_length because it is not mandatory */
1641 p_mmi->last_object.u.enq.psz_text = malloc( l + 1 );
1642 strncpy( p_mmi->last_object.u.enq.psz_text, (char *)d, l );
1643 p_mmi->last_object.u.enq.psz_text[l] = '\0';
1645 msg_Dbg( p_access, "MMI enq: %s%s", p_mmi->last_object.u.enq.psz_text,
1646 p_mmi->last_object.u.enq.b_blind == true ? " (blind)" : "" );
1647 p_sys->pb_slot_mmi_expected[i_slot] = false;
1648 p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
1651 /*****************************************************************************
1653 *****************************************************************************/
1654 static void MMIHandleMenu( access_t *p_access, int i_session_id, int i_tag,
1655 uint8_t *p_apdu, int i_size )
1657 access_sys_t *p_sys = p_access->p_sys;
1658 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1659 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1661 uint8_t *d = APDUGetLength( p_apdu, &l );
1663 en50221_MMIFree( &p_mmi->last_object );
1664 p_mmi->last_object.i_object_type = (i_tag == AOT_MENU_LAST) ?
1665 EN50221_MMI_MENU : EN50221_MMI_LIST;
1666 p_mmi->last_object.u.menu.i_choices = 0;
1667 p_mmi->last_object.u.menu.ppsz_choices = NULL;
1671 l--; d++; /* choice_nb */
1673 #define GET_FIELD( x ) \
1676 p_mmi->last_object.u.menu.psz_##x \
1677 = MMIGetText( p_access, &d, &l ); \
1678 msg_Dbg( p_access, "MMI " STRINGIFY( x ) ": %s", \
1679 p_mmi->last_object.u.menu.psz_##x ); \
1683 GET_FIELD( subtitle );
1684 GET_FIELD( bottom );
1689 char *psz_text = MMIGetText( p_access, &d, &l );
1690 TAB_APPEND( p_mmi->last_object.u.menu.i_choices,
1691 p_mmi->last_object.u.menu.ppsz_choices,
1693 msg_Dbg( p_access, "MMI choice: %s", psz_text );
1696 p_sys->pb_slot_mmi_expected[i_slot] = false;
1697 p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
1700 /*****************************************************************************
1702 *****************************************************************************/
1703 static void MMIHandle( access_t *p_access, int i_session_id,
1704 uint8_t *p_apdu, int i_size )
1706 int i_tag = APDUGetTag( p_apdu, i_size );
1710 case AOT_DISPLAY_CONTROL:
1713 uint8_t *d = APDUGetLength( p_apdu, &l );
1719 case DCC_SET_MMI_MODE:
1720 if ( l == 2 && d[1] == MM_HIGH_LEVEL )
1721 MMIDisplayReply( p_access, i_session_id );
1723 msg_Err( p_access, "unsupported MMI mode %02x", d[1] );
1727 msg_Err( p_access, "unsupported display control command %02x",
1736 MMIHandleEnq( p_access, i_session_id, p_apdu, i_size );
1741 MMIHandleMenu( p_access, i_session_id, i_tag, p_apdu, i_size );
1745 SessionSendClose( p_access, i_session_id );
1749 msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
1753 /*****************************************************************************
1755 *****************************************************************************/
1756 static void MMIClose( access_t *p_access, int i_session_id )
1758 access_sys_t *p_sys = p_access->p_sys;
1759 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1760 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1762 en50221_MMIFree( &p_mmi->last_object );
1763 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1765 msg_Dbg( p_access, "closing MMI session (%d)", i_session_id );
1766 p_sys->pb_slot_mmi_expected[i_slot] = false;
1767 p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
1770 /*****************************************************************************
1772 *****************************************************************************/
1773 static void MMIOpen( access_t *p_access, int i_session_id )
1775 access_sys_t *p_sys = p_access->p_sys;
1778 msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
1780 p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
1781 p_sys->p_sessions[i_session_id - 1].pf_close = MMIClose;
1782 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(mmi_t));
1783 p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1784 p_mmi->last_object.i_object_type = EN50221_MMI_NONE;
1792 /*****************************************************************************
1793 * InitSlot: Open the transport layer
1794 *****************************************************************************/
1795 #define MAX_TC_RETRIES 20
1797 static int InitSlot( access_t * p_access, int i_slot )
1799 access_sys_t *p_sys = p_access->p_sys;
1802 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1805 msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
1807 return VLC_EGENERIC;
1810 /* This is out of the spec */
1811 for ( i = 0; i < MAX_TC_RETRIES; i++ )
1814 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
1815 && i_tag == T_CTC_REPLY )
1817 p_sys->pb_active_slot[i_slot] = true;
1821 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1825 "en50221_Init: couldn't send TPDU on slot %d",
1831 if ( p_sys->pb_active_slot[i_slot] )
1833 p_sys->i_ca_timeout = 100000;
1837 return VLC_EGENERIC;
1842 * External entry points
1845 /*****************************************************************************
1846 * en50221_Init : Initialize the CAM for en50221
1847 *****************************************************************************/
1848 int en50221_Init( access_t * p_access )
1850 access_sys_t *p_sys = p_access->p_sys;
1852 if( p_sys->i_ca_type & CA_CI_LINK )
1855 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1857 if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
1859 msg_Err( p_access, "en50221_Init: couldn't reset slot %d",
1864 p_sys->i_ca_timeout = 100000;
1865 /* Wait a bit otherwise it doesn't initialize properly... */
1872 struct ca_slot_info info;
1875 /* We don't reset the CAM in that case because it's done by the
1877 if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &info ) < 0 )
1879 msg_Err( p_access, "en50221_Init: couldn't get slot info" );
1880 close( p_sys->i_ca_handle );
1881 p_sys->i_ca_handle = 0;
1882 return VLC_EGENERIC;
1884 if( info.flags == 0 )
1886 msg_Err( p_access, "en50221_Init: no CAM inserted" );
1887 close( p_sys->i_ca_handle );
1888 p_sys->i_ca_handle = 0;
1889 return VLC_EGENERIC;
1892 /* Allocate a dummy sessions */
1893 p_sys->p_sessions[ 0 ].i_resource_id = RI_CONDITIONAL_ACCESS_SUPPORT;
1895 /* Get application info to find out which cam we are using and make
1896 sure everything is ready to play */
1899 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1900 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1901 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1902 memset( &ca_msg.msg[3], 0, 253 );
1903 APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1904 if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1906 msg_Err( p_access, "en50221_Init: failed getting message" );
1907 return VLC_EGENERIC;
1910 #if HLCI_WAIT_CAM_READY
1911 while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1913 if( p_access->b_die ) return VLC_EGENERIC;
1915 msg_Dbg( p_access, "CAM: please wait" );
1916 APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1918 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1919 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1920 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1921 memset( &ca_msg.msg[3], 0, 253 );
1922 if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1924 msg_Err( p_access, "en50221_Init: failed getting message" );
1925 return VLC_EGENERIC;
1927 msg_Dbg( p_access, "en50221_Init: Got length: %d, tag: 0x%x", ca_msg.length, APDUGetTag( ca_msg.msg, ca_msg.length ) );
1930 if( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1932 msg_Err( p_access, "CAM returns garbage as application info!" );
1933 return VLC_EGENERIC;
1936 msg_Dbg( p_access, "found CAM %s using id 0x%x", &ca_msg.msg[12],
1937 (ca_msg.msg[8]<<8)|ca_msg.msg[9] );
1942 /*****************************************************************************
1943 * en50221_Poll : Poll the CAM for TPDUs
1944 *****************************************************************************/
1945 int en50221_Poll( access_t * p_access )
1947 access_sys_t *p_sys = p_access->p_sys;
1951 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1954 ca_slot_info_t sinfo;
1957 if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
1959 msg_Err( p_access, "en50221_Poll: couldn't get info on slot %d",
1964 if ( !(sinfo.flags & CA_CI_MODULE_READY) )
1966 if ( p_sys->pb_active_slot[i_slot] )
1968 msg_Dbg( p_access, "en50221_Poll: slot %d has been removed",
1970 p_sys->pb_active_slot[i_slot] = false;
1971 p_sys->pb_slot_mmi_expected[i_slot] = false;
1972 p_sys->pb_slot_mmi_undisplayed[i_slot] = false;
1974 /* Close all sessions for this slot. */
1975 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS;
1978 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1979 && p_sys->p_sessions[i_session_id - 1].i_slot
1982 if ( p_sys->p_sessions[i_session_id - 1].pf_close
1985 p_sys->p_sessions[i_session_id - 1].pf_close(
1986 p_access, i_session_id );
1988 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
1995 else if ( !p_sys->pb_active_slot[i_slot] )
1997 InitSlot( p_access, i_slot );
1999 if ( !p_sys->pb_active_slot[i_slot] )
2001 msg_Dbg( p_access, "en50221_Poll: resetting slot %d", i_slot );
2003 if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
2005 msg_Err( p_access, "en50221_Poll: couldn't reset slot %d",
2011 msg_Dbg( p_access, "en50221_Poll: slot %d is active",
2015 if ( !p_sys->pb_tc_has_data[i_slot] )
2017 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, NULL, 0 ) !=
2021 "en50221_Poll: couldn't send TPDU on slot %d",
2025 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) !=
2029 "en50221_Poll: couldn't recv TPDU on slot %d",
2035 while ( p_sys->pb_tc_has_data[i_slot] )
2037 uint8_t p_tpdu[MAX_TPDU_SIZE];
2038 int i_size, i_session_size;
2041 if ( TPDUSend( p_access, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
2044 "en50221_Poll: couldn't send TPDU on slot %d",
2048 if ( TPDURecv( p_access, i_slot, &i_tag, p_tpdu, &i_size ) !=
2052 "en50221_Poll: couldn't recv TPDU on slot %d",
2057 p_session = GetLength( &p_tpdu[3], &i_session_size );
2058 if ( i_session_size <= 1 )
2064 if ( i_tag != T_DATA_LAST )
2067 "en50221_Poll: fragmented TPDU not supported" );
2071 SPDUHandle( p_access, i_slot, p_session, i_session_size );
2075 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2077 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2078 && p_sys->p_sessions[i_session_id - 1].pf_manage )
2080 p_sys->p_sessions[i_session_id - 1].pf_manage( p_access,
2089 /*****************************************************************************
2090 * en50221_SetCAPMT :
2091 *****************************************************************************/
2092 int en50221_SetCAPMT( access_t * p_access, dvbpsi_pmt_t *p_pmt )
2094 access_sys_t *p_sys = p_access->p_sys;
2095 int i, i_session_id;
2096 bool b_update = false;
2097 bool b_needs_descrambling = CAPMTNeedsDescrambling( p_pmt );
2099 for ( i = 0; i < MAX_PROGRAMS; i++ )
2101 if ( p_sys->pp_selected_programs[i] != NULL
2102 && p_sys->pp_selected_programs[i]->i_program_number
2103 == p_pmt->i_program_number )
2107 if ( !b_needs_descrambling )
2109 dvbpsi_DeletePMT( p_pmt );
2110 p_pmt = p_sys->pp_selected_programs[i];
2111 p_sys->pp_selected_programs[i] = NULL;
2113 else if( p_pmt != p_sys->pp_selected_programs[i] )
2115 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
2116 p_sys->pp_selected_programs[i] = p_pmt;
2123 if ( !b_update && b_needs_descrambling )
2125 for ( i = 0; i < MAX_PROGRAMS; i++ )
2127 if ( p_sys->pp_selected_programs[i] == NULL )
2129 p_sys->pp_selected_programs[i] = p_pmt;
2135 if ( b_update || b_needs_descrambling )
2137 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2139 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2140 == RI_CONDITIONAL_ACCESS_SUPPORT )
2142 if ( b_update && b_needs_descrambling )
2143 CAPMTUpdate( p_access, i_session_id, p_pmt );
2144 else if ( b_update )
2145 CAPMTDelete( p_access, i_session_id, p_pmt );
2147 CAPMTAdd( p_access, i_session_id, p_pmt );
2152 if ( !b_needs_descrambling )
2154 dvbpsi_DeletePMT( p_pmt );
2160 /*****************************************************************************
2162 *****************************************************************************/
2163 int en50221_OpenMMI( access_t * p_access, int i_slot )
2165 access_sys_t *p_sys = p_access->p_sys;
2167 if( p_sys->i_ca_type & CA_CI_LINK )
2170 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2172 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2173 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2176 "MMI menu is already opened on slot %d (session=%d)",
2177 i_slot, i_session_id );
2182 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2184 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2185 == RI_APPLICATION_INFORMATION
2186 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2188 ApplicationInformationEnterMenu( p_access, i_session_id );
2193 msg_Err( p_access, "no application information on slot %d", i_slot );
2194 return VLC_EGENERIC;
2198 msg_Err( p_access, "MMI menu not supported" );
2199 return VLC_EGENERIC;
2203 /*****************************************************************************
2204 * en50221_CloseMMI :
2205 *****************************************************************************/
2206 int en50221_CloseMMI( access_t * p_access, int i_slot )
2208 access_sys_t *p_sys = p_access->p_sys;
2210 if( p_sys->i_ca_type & CA_CI_LINK )
2213 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2215 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2216 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2218 MMISendClose( p_access, i_session_id );
2223 msg_Warn( p_access, "closing a non-existing MMI session on slot %d",
2225 return VLC_EGENERIC;
2229 msg_Err( p_access, "MMI menu not supported" );
2230 return VLC_EGENERIC;
2234 /*****************************************************************************
2235 * en50221_GetMMIObject :
2236 *****************************************************************************/
2237 en50221_mmi_object_t *en50221_GetMMIObject( access_t * p_access,
2240 access_sys_t *p_sys = p_access->p_sys;
2243 if ( p_sys->pb_slot_mmi_expected[i_slot] == true )
2244 return NULL; /* should not happen */
2246 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2248 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2249 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2252 (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
2253 if ( p_mmi == NULL )
2254 return NULL; /* should not happen */
2255 return &p_mmi->last_object;
2263 /*****************************************************************************
2264 * en50221_SendMMIObject :
2265 *****************************************************************************/
2266 void en50221_SendMMIObject( access_t * p_access, int i_slot,
2267 en50221_mmi_object_t *p_object )
2269 access_sys_t *p_sys = p_access->p_sys;
2272 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2274 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2275 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2277 MMISendObject( p_access, i_session_id, p_object );
2282 msg_Err( p_access, "SendMMIObject when no MMI session is opened !" );
2285 /*****************************************************************************
2287 *****************************************************************************/
2288 void en50221_End( access_t * p_access )
2290 access_sys_t *p_sys = p_access->p_sys;
2291 int i_session_id, i;
2293 for ( i = 0; i < MAX_PROGRAMS; i++ )
2295 if ( p_sys->pp_selected_programs[i] != NULL )
2297 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
2301 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2303 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2304 && p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
2306 p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
2311 /* Leave the CAM configured, so that it can be reused in another
2315 static inline void *FixUTF8( char *p )
2321 static char *dvbsi_to_utf8( char *psz_instring, size_t i_length )
2323 const char *psz_encoding;
2324 char *psz_stringstart, *psz_outstring, *psz_tmp;
2325 char psz_encbuf[12];
2327 vlc_iconv_t iconv_handle;
2328 if( i_length < 1 ) return NULL;
2329 if( psz_instring[0] < 0 || psz_instring[0] >= 0x20 )
2331 psz_stringstart = psz_instring;
2332 psz_encoding = "ISO_8859-1"; /* should be ISO6937 according to spec, but this seems to be the one used */
2333 } else switch( psz_instring[0] )
2336 psz_stringstart = &psz_instring[1];
2337 psz_encoding = "ISO_8859-5";
2340 psz_stringstart = &psz_instring[1];
2341 psz_encoding = "ISO_8859-6";
2344 psz_stringstart = &psz_instring[1];
2345 psz_encoding = "ISO_8859-7";
2348 psz_stringstart = &psz_instring[1];
2349 psz_encoding = "ISO_8859-8";
2352 psz_stringstart = &psz_instring[1];
2353 psz_encoding = "ISO_8859-9";
2356 psz_stringstart = &psz_instring[1];
2357 psz_encoding = "ISO_8859-10";
2360 psz_stringstart = &psz_instring[1];
2361 psz_encoding = "ISO_8859-11";
2364 psz_stringstart = &psz_instring[1]; /*possibly reserved?*/
2365 psz_encoding = "ISO_8859-12";
2368 psz_stringstart = &psz_instring[1];
2369 psz_encoding = "ISO_8859-13";
2372 psz_stringstart = &psz_instring[1];
2373 psz_encoding = "ISO_8859-14";
2376 psz_stringstart = &psz_instring[1];
2377 psz_encoding = "ISO_8859-15";
2380 if( i_length < 3 || psz_instring[1] != '\0' || psz_instring[2] > 0x0f
2381 || psz_instring[2] == 0 )
2382 return FixUTF8(strndup(psz_instring,i_length));
2383 sprintf( psz_encbuf, "ISO_8859-%d", psz_instring[2] );
2384 psz_stringstart = &psz_instring[3];
2385 psz_encoding = psz_encbuf;
2388 psz_stringstart = &psz_instring[1];
2389 psz_encoding = "UTF-16";
2392 psz_stringstart = &psz_instring[1];
2393 psz_encoding = "KSC5601-1987";
2396 psz_stringstart = &psz_instring[1];
2397 psz_encoding = "GB2312";/*GB-2312-1980 */
2400 psz_stringstart = &psz_instring[1];
2401 psz_encoding = "BIG-5";
2404 return FixUTF8(strndup(&psz_instring[1],i_length-1));
2408 return FixUTF8(strndup(psz_instring,i_length));
2410 iconv_handle = vlc_iconv_open( "UTF-8", psz_encoding );
2411 i_in = i_length - (psz_stringstart - psz_instring );
2413 psz_outstring = psz_tmp = (char*)malloc( i_out * sizeof(char) + 1 );
2414 vlc_iconv( iconv_handle, &psz_stringstart, &i_in, &psz_tmp, &i_out );
2415 vlc_iconv_close( iconv_handle );
2417 return psz_outstring;