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>
60 # include <dvbpsi/nit.h>
63 # include "descriptor.h"
64 # include "tables/pat.h"
65 # include "tables/pmt.h"
66 # include "descriptors/dr.h"
74 # include "vlc_httpd.h"
79 #include <vlc_charset.h>
82 #define HLCI_WAIT_CAM_READY 0
83 #define CAM_PROG_MAX MAX_PROGRAMS
85 static void ResourceManagerOpen( access_t * p_access, int i_session_id );
86 static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
87 static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
88 static void DateTimeOpen( access_t * p_access, int i_session_id );
89 static void MMIOpen( access_t * p_access, int i_session_id );
91 /*****************************************************************************
93 *****************************************************************************/
94 #define SIZE_INDICATOR 0x80
96 static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
98 *pi_length = *p_data++;
100 if ( (*pi_length & SIZE_INDICATOR) != 0 )
102 int l = *pi_length & ~SIZE_INDICATOR;
106 for ( i = 0; i < l; i++ )
107 *pi_length = (*pi_length << 8) | *p_data++;
113 static uint8_t *SetLength( uint8_t *p_data, int i_length )
117 if ( i_length < 128 )
121 else if ( i_length < 256 )
123 *p++ = SIZE_INDICATOR | 0x1;
126 else if ( i_length < 65536 )
128 *p++ = SIZE_INDICATOR | 0x2;
129 *p++ = i_length >> 8;
130 *p++ = i_length & 0xff;
132 else if ( i_length < 16777216 )
134 *p++ = SIZE_INDICATOR | 0x3;
135 *p++ = i_length >> 16;
136 *p++ = (i_length >> 8) & 0xff;
137 *p++ = i_length & 0xff;
141 *p++ = SIZE_INDICATOR | 0x4;
142 *p++ = i_length >> 24;
143 *p++ = (i_length >> 16) & 0xff;
144 *p++ = (i_length >> 8) & 0xff;
145 *p++ = i_length & 0xff;
156 #define MAX_TPDU_SIZE 2048
157 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
159 #define DATA_INDICATOR 0x80
163 #define T_CREATE_TC 0x82
164 #define T_CTC_REPLY 0x83
165 #define T_DELETE_TC 0x84
166 #define T_DTC_REPLY 0x85
167 #define T_REQUEST_TC 0x86
168 #define T_NEW_TC 0x87
169 #define T_TC_ERROR 0x88
170 #define T_DATA_LAST 0xA0
171 #define T_DATA_MORE 0xA1
173 static void Dump( bool b_outgoing, uint8_t *p_data, int i_size )
178 fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
179 for ( i = 0; i < i_size && i < MAX_DUMP; i++)
180 fprintf(stderr, "%02X ", p_data[i]);
181 fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
183 VLC_UNUSED(b_outgoing); VLC_UNUSED(p_data); VLC_UNUSED(i_size);
187 /*****************************************************************************
189 *****************************************************************************/
190 static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
191 const uint8_t *p_content, int i_length )
193 access_sys_t *p_sys = p_access->p_sys;
194 uint8_t i_tcid = i_slot + 1;
195 uint8_t p_data[MAX_TPDU_SIZE];
211 p_data[3] = 1; /* length */
218 p_data[3] = 2; /* length */
220 p_data[5] = p_content[0];
227 /* i_length <= MAX_TPDU_DATA */
228 uint8_t *p = p_data + 3;
229 p = SetLength( p, i_length + 1 );
233 memcpy( p, p_content, i_length );
234 i_size = i_length + (p - p_data);
241 Dump( true, p_data, i_size );
243 if ( write( p_sys->i_ca_handle, p_data, i_size ) != i_size )
245 msg_Err( p_access, "cannot write to CAM device (%m)" );
253 /*****************************************************************************
255 *****************************************************************************/
256 #define CAM_READ_TIMEOUT 3500 // ms
258 static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
259 uint8_t *p_data, int *pi_size )
261 access_sys_t *p_sys = p_access->p_sys;
262 uint8_t i_tcid = i_slot + 1;
264 struct pollfd pfd[1];
266 pfd[0].fd = p_sys->i_ca_handle;
267 pfd[0].events = POLLIN;
268 if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
270 msg_Err( p_access, "cannot poll from CAM device" );
274 if ( pi_size == NULL )
276 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 );
293 if ( p_data[1] != i_tcid )
295 msg_Err( p_access, "invalid read from CAM device (%d instead of %d)",
301 p_sys->pb_tc_has_data[i_slot] = (i_size >= 4
302 && p_data[i_size - 4] == T_SB
303 && p_data[i_size - 3] == 2
304 && (p_data[i_size - 1] & DATA_INDICATOR))
307 Dump( false, p_data, i_size );
309 if ( pi_size == NULL )
322 #define ST_SESSION_NUMBER 0x90
323 #define ST_OPEN_SESSION_REQUEST 0x91
324 #define ST_OPEN_SESSION_RESPONSE 0x92
325 #define ST_CREATE_SESSION 0x93
326 #define ST_CREATE_SESSION_RESPONSE 0x94
327 #define ST_CLOSE_SESSION_REQUEST 0x95
328 #define ST_CLOSE_SESSION_RESPONSE 0x96
331 #define SS_NOT_ALLOCATED 0xF0
333 #define RI_RESOURCE_MANAGER 0x00010041
334 #define RI_APPLICATION_INFORMATION 0x00020041
335 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
336 #define RI_HOST_CONTROL 0x00200041
337 #define RI_DATE_TIME 0x00240041
338 #define RI_MMI 0x00400041
340 static int ResourceIdToInt( uint8_t *p_data )
342 return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
343 | ((int)p_data[2] << 8) | p_data[3];
346 /*****************************************************************************
348 *****************************************************************************/
349 static int SPDUSend( access_t * p_access, int i_session_id,
350 uint8_t *p_data, int i_size )
352 access_sys_t *p_sys = p_access->p_sys;
353 uint8_t *p_spdu = malloc( i_size + 4 );
356 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
358 *p++ = ST_SESSION_NUMBER;
360 *p++ = (i_session_id >> 8);
361 *p++ = i_session_id & 0xff;
363 memcpy( p, p_data, i_size );
370 if ( i_size > MAX_TPDU_DATA )
372 if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
373 MAX_TPDU_DATA ) != VLC_SUCCESS )
375 msg_Err( p_access, "couldn't send TPDU on session %d",
381 i_size -= MAX_TPDU_DATA;
385 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
388 msg_Err( p_access, "couldn't send TPDU on session %d",
396 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
399 msg_Err( p_access, "couldn't recv TPDU on session %d",
410 /*****************************************************************************
412 *****************************************************************************/
413 static void SessionOpen( access_t * p_access, uint8_t i_slot,
414 uint8_t *p_spdu, int i_size )
416 access_sys_t *p_sys = p_access->p_sys;
418 int i_resource_id = ResourceIdToInt( &p_spdu[2] );
419 uint8_t p_response[16];
420 int i_status = SS_NOT_ALLOCATED;
423 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
425 if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
428 if ( i_session_id == MAX_SESSIONS )
430 msg_Err( p_access, "too many sessions !" );
433 p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
434 p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
435 p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
436 p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
438 if ( i_resource_id == RI_RESOURCE_MANAGER
439 || i_resource_id == RI_APPLICATION_INFORMATION
440 || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
441 || i_resource_id == RI_DATE_TIME
442 || i_resource_id == RI_MMI )
447 p_response[0] = ST_OPEN_SESSION_RESPONSE;
449 p_response[2] = i_status;
450 p_response[3] = p_spdu[2];
451 p_response[4] = p_spdu[3];
452 p_response[5] = p_spdu[4];
453 p_response[6] = p_spdu[5];
454 p_response[7] = i_session_id >> 8;
455 p_response[8] = i_session_id & 0xff;
457 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) !=
461 "SessionOpen: couldn't send TPDU on slot %d", i_slot );
464 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
467 "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
471 switch ( i_resource_id )
473 case RI_RESOURCE_MANAGER:
474 ResourceManagerOpen( p_access, i_session_id ); break;
475 case RI_APPLICATION_INFORMATION:
476 ApplicationInformationOpen( p_access, i_session_id ); break;
477 case RI_CONDITIONAL_ACCESS_SUPPORT:
478 ConditionalAccessOpen( p_access, i_session_id ); break;
480 DateTimeOpen( p_access, i_session_id ); break;
482 MMIOpen( p_access, i_session_id ); break;
484 case RI_HOST_CONTROL:
486 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
487 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
492 /* unused code for the moment - commented out to keep gcc happy */
493 /*****************************************************************************
495 *****************************************************************************/
496 static void SessionCreate( access_t * p_access, int i_slot, int i_resource_id )
498 access_sys_t *p_sys = p_access->p_sys;
499 uint8_t p_response[16];
503 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
505 if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
508 if ( i_session_id == MAX_SESSIONS )
510 msg_Err( p_access, "too many sessions !" );
513 p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
514 p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
515 p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
516 p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
517 p_sys->p_sessions[i_session_id - 1].p_sys = NULL;
519 p_response[0] = ST_CREATE_SESSION;
521 p_response[2] = i_resource_id >> 24;
522 p_response[3] = (i_resource_id >> 16) & 0xff;
523 p_response[4] = (i_resource_id >> 8) & 0xff;
524 p_response[5] = i_resource_id & 0xff;
525 p_response[6] = i_session_id >> 8;
526 p_response[7] = i_session_id & 0xff;
528 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
532 "SessionCreate: couldn't send TPDU on slot %d", i_slot );
535 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
538 "SessionCreate: couldn't recv TPDU on slot %d", i_slot );
544 /*****************************************************************************
545 * SessionCreateResponse
546 *****************************************************************************/
547 static void SessionCreateResponse( access_t * p_access, uint8_t i_slot,
548 uint8_t *p_spdu, int i_size )
550 access_sys_t *p_sys = p_access->p_sys;
551 int i_status = p_spdu[2];
552 int i_resource_id = ResourceIdToInt( &p_spdu[3] );
553 int i_session_id = ((int)p_spdu[7] << 8) | p_spdu[8];
555 if ( i_status != SS_OK )
557 msg_Err( p_access, "SessionCreateResponse: failed to open session %d"
558 " resource=0x%x status=0x%x", i_session_id, i_resource_id,
560 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
564 switch ( i_resource_id )
566 case RI_RESOURCE_MANAGER:
567 ResourceManagerOpen( p_access, i_session_id ); break;
568 case RI_APPLICATION_INFORMATION:
569 ApplicationInformationOpen( p_access, i_session_id ); break;
570 case RI_CONDITIONAL_ACCESS_SUPPORT:
571 ConditionalAccessOpen( p_access, i_session_id ); break;
573 DateTimeOpen( p_access, i_session_id ); break;
575 MMIOpen( p_access, i_session_id ); break;
577 case RI_HOST_CONTROL:
579 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
580 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
584 /*****************************************************************************
586 *****************************************************************************/
587 static void SessionSendClose( access_t * p_access, int i_session_id )
589 access_sys_t *p_sys = p_access->p_sys;
590 uint8_t p_response[16];
592 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
594 p_response[0] = ST_CLOSE_SESSION_REQUEST;
596 p_response[2] = i_session_id >> 8;
597 p_response[3] = i_session_id & 0xff;
599 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
603 "SessionSendClose: couldn't send TPDU on slot %d", i_slot );
606 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
609 "SessionSendClose: couldn't recv TPDU on slot %d", i_slot );
614 /*****************************************************************************
616 *****************************************************************************/
617 static void SessionClose( access_t * p_access, int i_session_id )
619 access_sys_t *p_sys = p_access->p_sys;
620 uint8_t p_response[16];
622 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
624 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
625 p_sys->p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
626 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
628 p_response[0] = ST_CLOSE_SESSION_RESPONSE;
630 p_response[2] = SS_OK;
631 p_response[3] = i_session_id >> 8;
632 p_response[4] = i_session_id & 0xff;
634 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
638 "SessionClose: couldn't send TPDU on slot %d", i_slot );
641 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
644 "SessionClose: couldn't recv TPDU on slot %d", i_slot );
649 /*****************************************************************************
651 *****************************************************************************/
652 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
653 uint8_t *p_spdu, int i_size )
655 access_sys_t *p_sys = p_access->p_sys;
660 case ST_SESSION_NUMBER:
663 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
664 p_sys->p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
665 p_spdu + 4, i_size - 4 );
668 case ST_OPEN_SESSION_REQUEST:
669 if ( i_size != 6 || p_spdu[1] != 0x4 )
671 SessionOpen( p_access, i_slot, p_spdu, i_size );
674 case ST_CREATE_SESSION_RESPONSE:
675 if ( i_size != 9 || p_spdu[1] != 0x7 )
677 SessionCreateResponse( p_access, i_slot, p_spdu, i_size );
680 case ST_CLOSE_SESSION_REQUEST:
681 if ( i_size != 4 || p_spdu[1] != 0x2 )
683 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
684 SessionClose( p_access, i_session_id );
687 case ST_CLOSE_SESSION_RESPONSE:
688 if ( i_size != 5 || p_spdu[1] != 0x3 )
690 i_session_id = ((int)p_spdu[3] << 8) | p_spdu[4];
693 msg_Err( p_access, "closing a session which is not allocated (%d)",
698 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
699 p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
701 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
706 msg_Err( p_access, "unexpected tag in SPDUHandle (%x)", p_spdu[0] );
716 #define AOT_NONE 0x000000
717 #define AOT_PROFILE_ENQ 0x9F8010
718 #define AOT_PROFILE 0x9F8011
719 #define AOT_PROFILE_CHANGE 0x9F8012
720 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
721 #define AOT_APPLICATION_INFO 0x9F8021
722 #define AOT_ENTER_MENU 0x9F8022
723 #define AOT_CA_INFO_ENQ 0x9F8030
724 #define AOT_CA_INFO 0x9F8031
725 #define AOT_CA_PMT 0x9F8032
726 #define AOT_CA_PMT_REPLY 0x9F8033
727 #define AOT_TUNE 0x9F8400
728 #define AOT_REPLACE 0x9F8401
729 #define AOT_CLEAR_REPLACE 0x9F8402
730 #define AOT_ASK_RELEASE 0x9F8403
731 #define AOT_DATE_TIME_ENQ 0x9F8440
732 #define AOT_DATE_TIME 0x9F8441
733 #define AOT_CLOSE_MMI 0x9F8800
734 #define AOT_DISPLAY_CONTROL 0x9F8801
735 #define AOT_DISPLAY_REPLY 0x9F8802
736 #define AOT_TEXT_LAST 0x9F8803
737 #define AOT_TEXT_MORE 0x9F8804
738 #define AOT_KEYPAD_CONTROL 0x9F8805
739 #define AOT_KEYPRESS 0x9F8806
740 #define AOT_ENQ 0x9F8807
741 #define AOT_ANSW 0x9F8808
742 #define AOT_MENU_LAST 0x9F8809
743 #define AOT_MENU_MORE 0x9F880A
744 #define AOT_MENU_ANSW 0x9F880B
745 #define AOT_LIST_LAST 0x9F880C
746 #define AOT_LIST_MORE 0x9F880D
747 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
748 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
749 #define AOT_DISPLAY_MESSAGE 0x9F8810
750 #define AOT_SCENE_END_MARK 0x9F8811
751 #define AOT_SCENE_DONE 0x9F8812
752 #define AOT_SCENE_CONTROL 0x9F8813
753 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
754 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
755 #define AOT_FLUSH_DOWNLOAD 0x9F8816
756 #define AOT_DOWNLOAD_REPLY 0x9F8817
757 #define AOT_COMMS_CMD 0x9F8C00
758 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
759 #define AOT_COMMS_REPLY 0x9F8C02
760 #define AOT_COMMS_SEND_LAST 0x9F8C03
761 #define AOT_COMMS_SEND_MORE 0x9F8C04
762 #define AOT_COMMS_RCV_LAST 0x9F8C05
763 #define AOT_COMMS_RCV_MORE 0x9F8C06
765 /*****************************************************************************
767 *****************************************************************************/
768 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
773 for ( i = 0; i < 3; i++ )
774 t = (t << 8) | *p_apdu++;
781 /*****************************************************************************
783 *****************************************************************************/
784 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
786 return GetLength( &p_apdu[3], pi_size );
789 /*****************************************************************************
791 *****************************************************************************/
792 static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
793 uint8_t *p_data, int i_size )
795 access_sys_t *p_sys = p_access->p_sys;
796 uint8_t *p_apdu = malloc( i_size + 12 );
801 *p++ = (i_tag >> 16);
802 *p++ = (i_tag >> 8) & 0xff;
804 p = SetLength( p, i_size );
806 memcpy( p, p_data, i_size );
807 if ( p_sys->i_ca_type == CA_CI_LINK )
809 i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
813 if ( i_size + p - p_apdu > 256 )
815 msg_Err( p_access, "CAM: apdu overflow" );
816 i_ret = VLC_EGENERIC;
821 ca_msg.length = i_size + p - p_apdu;
822 if ( i_size == 0 ) ca_msg.length=3;
823 psz_hex = (char*)malloc( ca_msg.length*3 + 1);
824 memcpy( ca_msg.msg, p_apdu, i_size + p - p_apdu );
825 i_ret = ioctl(p_sys->i_ca_handle, CA_SEND_MSG, &ca_msg );
828 msg_Err( p_access, "Error sending to CAM: %m" );
829 i_ret = VLC_EGENERIC;
841 /*****************************************************************************
842 * ResourceManagerHandle
843 *****************************************************************************/
844 static void ResourceManagerHandle( access_t * p_access, int i_session_id,
845 uint8_t *p_apdu, int i_size )
847 int i_tag = APDUGetTag( p_apdu, i_size );
851 case AOT_PROFILE_ENQ:
853 int resources[] = { htonl(RI_RESOURCE_MANAGER),
854 htonl(RI_APPLICATION_INFORMATION),
855 htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
859 APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
864 APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
868 msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
873 /*****************************************************************************
874 * ResourceManagerOpen
875 *****************************************************************************/
876 static void ResourceManagerOpen( access_t * p_access, int i_session_id )
878 access_sys_t *p_sys = p_access->p_sys;
880 msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
882 p_sys->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
884 APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
888 * Application Information
891 /*****************************************************************************
892 * ApplicationInformationEnterMenu
893 *****************************************************************************/
894 static void ApplicationInformationEnterMenu( access_t * p_access,
897 access_sys_t *p_sys = p_access->p_sys;
898 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
900 msg_Dbg( p_access, "entering MMI menus on session %d", i_session_id );
901 APDUSend( p_access, i_session_id, AOT_ENTER_MENU, NULL, 0 );
902 p_sys->pb_slot_mmi_expected[i_slot] = true;
905 /*****************************************************************************
906 * ApplicationInformationHandle
907 *****************************************************************************/
908 static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
909 uint8_t *p_apdu, int i_size )
911 int i_tag = APDUGetTag( p_apdu, i_size );
915 case AOT_APPLICATION_INFO:
917 int i_type, i_manufacturer, i_code;
919 uint8_t *d = APDUGetLength( p_apdu, &l );
922 p_apdu[l + 4] = '\0';
925 i_manufacturer = ((int)d[0] << 8) | d[1];
927 i_code = ((int)d[0] << 8) | d[1];
929 d = GetLength( d, &l );
931 msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
932 d, i_type, i_manufacturer, i_code );
937 "unexpected tag in ApplicationInformationHandle (0x%x)",
942 /*****************************************************************************
943 * ApplicationInformationOpen
944 *****************************************************************************/
945 static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
947 access_sys_t *p_sys = p_access->p_sys;
949 msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
951 p_sys->p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
953 APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
960 #define MAX_CASYSTEM_IDS 16
964 uint16_t pi_system_ids[MAX_CASYSTEM_IDS + 1];
967 static bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
970 if( !p_ids ) return true;
972 while ( p_ids->pi_system_ids[i] )
974 if ( p_ids->pi_system_ids[i] == i_id )
982 /*****************************************************************************
983 * CAPMTNeedsDescrambling
984 *****************************************************************************/
985 static bool CAPMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
987 dvbpsi_descriptor_t *p_dr;
988 dvbpsi_pmt_es_t *p_es;
990 for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
992 if( p_dr->i_tag == 0x9 )
998 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1000 for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
1001 p_dr = p_dr->p_next )
1003 if( p_dr->i_tag == 0x9 )
1013 /*****************************************************************************
1015 *****************************************************************************/
1016 static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr )
1020 while ( p_dr != NULL )
1022 if( p_dr->i_tag == 0x9 )
1024 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1026 if ( CheckSystemID( p_ids, i_sysid ) )
1027 i_cad_size += p_dr->i_length + 2;
1029 p_dr = p_dr->p_next;
1035 static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt,
1036 uint16_t i_program_number, uint8_t i_version,
1037 int i_size, dvbpsi_descriptor_t *p_dr,
1043 p_data = malloc( 7 + i_size );
1045 p_data = malloc( 6 );
1047 p_data[0] = i_list_mgt;
1048 p_data[1] = i_program_number >> 8;
1049 p_data[2] = i_program_number & 0xff;
1050 p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
1056 p_data[4] = (i_size + 1) >> 8;
1057 p_data[5] = (i_size + 1) & 0xff;
1061 while ( p_dr != NULL )
1063 if( p_dr->i_tag == 0x9 )
1065 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1067 if ( CheckSystemID( p_ids, i_sysid ) )
1070 p_data[i + 1] = p_dr->i_length;
1071 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1072 // p_data[i+4] &= 0x1f;
1073 i += p_dr->i_length + 2;
1076 p_dr = p_dr->p_next;
1088 static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt,
1089 int i_capmt_size, uint8_t i_type, uint16_t i_pid,
1090 int i_size, dvbpsi_descriptor_t *p_dr,
1097 p_data = realloc( p_capmt, i_capmt_size + 6 + i_size );
1099 p_data = realloc( p_capmt, i_capmt_size + 5 );
1104 p_data[i + 1] = i_pid >> 8;
1105 p_data[i + 2] = i_pid & 0xff;
1109 p_data[i + 3] = (i_size + 1) >> 8;
1110 p_data[i + 4] = (i_size + 1) & 0xff;
1111 p_data[i + 5] = i_cmd;
1114 while ( p_dr != NULL )
1116 if( p_dr->i_tag == 0x9 )
1118 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1120 if ( CheckSystemID( p_ids, i_sysid ) )
1123 p_data[i + 1] = p_dr->i_length;
1124 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1125 i += p_dr->i_length + 2;
1128 p_dr = p_dr->p_next;
1140 static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id,
1141 dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt,
1142 uint8_t i_cmd, int *pi_capmt_size )
1144 access_sys_t *p_sys = p_access->p_sys;
1145 system_ids_t *p_ids =
1146 (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1147 dvbpsi_pmt_es_t *p_es;
1148 int i_cad_size, i_cad_program_size;
1151 i_cad_size = i_cad_program_size =
1152 GetCADSize( p_ids, p_pmt->p_first_descriptor );
1153 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1155 i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor );
1161 "no compatible scrambling system for SID %d on session %d",
1162 p_pmt->i_program_number, i_session_id );
1167 p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number,
1168 p_pmt->i_version, i_cad_program_size,
1169 p_pmt->p_first_descriptor, i_cmd );
1171 if ( i_cad_program_size )
1172 *pi_capmt_size = 7 + i_cad_program_size;
1176 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1178 i_cad_size = GetCADSize( p_ids, p_es->p_first_descriptor );
1180 if ( i_cad_size || i_cad_program_size )
1182 p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type,
1183 p_es->i_pid, i_cad_size,
1184 p_es->p_first_descriptor, i_cmd );
1186 *pi_capmt_size += 6 + i_cad_size;
1188 *pi_capmt_size += 5;
1195 /*****************************************************************************
1197 *****************************************************************************/
1198 static void CAPMTFirst( access_t * p_access, int i_session_id,
1199 dvbpsi_pmt_t *p_pmt )
1204 msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d",
1205 p_pmt->i_program_number, i_session_id );
1207 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1208 0x3 /* only */, 0x1 /* ok_descrambling */,
1212 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1215 /*****************************************************************************
1217 *****************************************************************************/
1218 static void CAPMTAdd( access_t * p_access, int i_session_id,
1219 dvbpsi_pmt_t *p_pmt )
1224 if( p_access->p_sys->i_selected_programs >= CAM_PROG_MAX )
1226 msg_Warn( p_access, "Not adding CAPMT for SID %d, too many programs",
1227 p_pmt->i_program_number );
1230 p_access->p_sys->i_selected_programs++;
1231 if( p_access->p_sys->i_selected_programs == 1 )
1233 CAPMTFirst( p_access, i_session_id, p_pmt );
1238 msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
1239 p_pmt->i_program_number, i_session_id );
1241 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1242 0x4 /* add */, 0x1 /* ok_descrambling */,
1246 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1249 /*****************************************************************************
1251 *****************************************************************************/
1252 static void CAPMTUpdate( access_t * p_access, int i_session_id,
1253 dvbpsi_pmt_t *p_pmt )
1258 msg_Dbg( p_access, "updating CAPMT for SID %d on session %d",
1259 p_pmt->i_program_number, i_session_id );
1261 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1262 0x5 /* update */, 0x1 /* ok_descrambling */,
1266 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1269 /*****************************************************************************
1271 *****************************************************************************/
1272 static void CAPMTDelete( access_t * p_access, int i_session_id,
1273 dvbpsi_pmt_t *p_pmt )
1278 p_access->p_sys->i_selected_programs--;
1279 msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d",
1280 p_pmt->i_program_number, i_session_id );
1282 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1283 0x5 /* update */, 0x4 /* not selected */,
1287 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1290 /*****************************************************************************
1291 * ConditionalAccessHandle
1292 *****************************************************************************/
1293 static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
1294 uint8_t *p_apdu, int i_size )
1296 access_sys_t *p_sys = p_access->p_sys;
1297 system_ids_t *p_ids =
1298 (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1299 int i_tag = APDUGetTag( p_apdu, i_size );
1307 uint8_t *d = APDUGetLength( p_apdu, &l );
1308 msg_Dbg( p_access, "CA system IDs supported by the application :" );
1310 for ( i = 0; i < l / 2; i++ )
1312 p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
1314 msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] );
1316 p_ids->pi_system_ids[i] = 0;
1318 for ( i = 0; i < MAX_PROGRAMS; i++ )
1320 if ( p_sys->pp_selected_programs[i] != NULL )
1322 CAPMTAdd( p_access, i_session_id,
1323 p_sys->pp_selected_programs[i] );
1331 "unexpected tag in ConditionalAccessHandle (0x%x)",
1336 /*****************************************************************************
1337 * ConditionalAccessClose
1338 *****************************************************************************/
1339 static void ConditionalAccessClose( access_t * p_access, int i_session_id )
1341 access_sys_t *p_sys = p_access->p_sys;
1343 msg_Dbg( p_access, "closing ConditionalAccess session (%d)", i_session_id );
1345 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1348 /*****************************************************************************
1349 * ConditionalAccessOpen
1350 *****************************************************************************/
1351 static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
1353 access_sys_t *p_sys = p_access->p_sys;
1355 msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
1357 p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
1358 p_sys->p_sessions[i_session_id - 1].pf_close = ConditionalAccessClose;
1359 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(system_ids_t));
1360 memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0,
1361 sizeof(system_ids_t) );
1363 APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
1376 /*****************************************************************************
1378 *****************************************************************************/
1379 static void DateTimeSend( access_t * p_access, int i_session_id )
1381 access_sys_t *p_sys = p_access->p_sys;
1382 date_time_t *p_date =
1383 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1385 time_t t = time(NULL);
1389 if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
1391 int Y = tm_gmt.tm_year;
1392 int M = tm_gmt.tm_mon + 1;
1393 int D = tm_gmt.tm_mday;
1394 int L = (M == 1 || M == 2) ? 1 : 0;
1395 int MJD = 14956 + D + (int)((Y - L) * 365.25)
1396 + (int)((M + 1 + L * 12) * 30.6001);
1397 uint8_t p_response[7];
1399 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
1401 p_response[0] = htons(MJD) >> 8;
1402 p_response[1] = htons(MJD) & 0xff;
1403 p_response[2] = DEC2BCD(tm_gmt.tm_hour);
1404 p_response[3] = DEC2BCD(tm_gmt.tm_min);
1405 p_response[4] = DEC2BCD(tm_gmt.tm_sec);
1406 p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
1407 p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
1409 APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
1411 p_date->i_last = mdate();
1415 /*****************************************************************************
1417 *****************************************************************************/
1418 static void DateTimeHandle( access_t * p_access, int i_session_id,
1419 uint8_t *p_apdu, int i_size )
1421 access_sys_t *p_sys = p_access->p_sys;
1422 date_time_t *p_date =
1423 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1425 int i_tag = APDUGetTag( p_apdu, i_size );
1429 case AOT_DATE_TIME_ENQ:
1432 const uint8_t *d = APDUGetLength( p_apdu, &l );
1436 p_date->i_interval = *d;
1437 msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
1438 p_date->i_interval );
1441 p_date->i_interval = 0;
1443 DateTimeSend( p_access, i_session_id );
1447 msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
1451 /*****************************************************************************
1453 *****************************************************************************/
1454 static void DateTimeManage( access_t * p_access, int i_session_id )
1456 access_sys_t *p_sys = p_access->p_sys;
1457 date_time_t *p_date =
1458 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1460 if ( p_date->i_interval
1461 && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
1463 DateTimeSend( p_access, i_session_id );
1467 /*****************************************************************************
1469 *****************************************************************************/
1470 static void DateTimeClose( access_t * p_access, int i_session_id )
1472 access_sys_t *p_sys = p_access->p_sys;
1474 msg_Dbg( p_access, "closing DateTime session (%d)", i_session_id );
1476 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1479 /*****************************************************************************
1481 *****************************************************************************/
1482 static void DateTimeOpen( access_t * p_access, int i_session_id )
1484 access_sys_t *p_sys = p_access->p_sys;
1486 msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
1488 p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
1489 p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
1490 p_sys->p_sessions[i_session_id - 1].pf_close = DateTimeClose;
1491 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t));
1492 memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
1494 DateTimeSend( p_access, i_session_id );
1501 /* Display Control Commands */
1503 #define DCC_SET_MMI_MODE 0x01
1504 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
1505 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
1506 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
1507 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
1511 #define MM_HIGH_LEVEL 0x01
1512 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
1513 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
1515 /* Display Reply IDs */
1517 #define DRI_MMI_MODE_ACK 0x01
1518 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
1519 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
1520 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
1521 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
1522 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
1523 #define DRI_UNKNOWN_MMI_MODE 0xF1
1524 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
1528 #define EF_BLIND 0x01
1532 #define AI_CANCEL 0x00
1533 #define AI_ANSWER 0x01
1537 en50221_mmi_object_t last_object;
1540 /*****************************************************************************
1542 *****************************************************************************/
1543 static void MMISendObject( access_t *p_access, int i_session_id,
1544 en50221_mmi_object_t *p_object )
1546 access_sys_t *p_sys = p_access->p_sys;
1547 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1551 switch ( p_object->i_object_type )
1553 case EN50221_MMI_ANSW:
1555 i_size = 1 + strlen( p_object->u.answ.psz_answ );
1556 p_data = malloc( i_size );
1557 p_data[0] = (p_object->u.answ.b_ok == true) ? 0x1 : 0x0;
1558 strncpy( (char *)&p_data[1], p_object->u.answ.psz_answ, i_size - 1 );
1561 case EN50221_MMI_MENU_ANSW:
1562 i_tag = AOT_MENU_ANSW;
1564 p_data = malloc( i_size );
1565 p_data[0] = p_object->u.menu_answ.i_choice;
1569 msg_Err( p_access, "unknown MMI object %d", p_object->i_object_type );
1573 APDUSend( p_access, i_session_id, i_tag, p_data, i_size );
1576 p_sys->pb_slot_mmi_expected[i_slot] = true;
1579 /*****************************************************************************
1581 *****************************************************************************/
1582 static void MMISendClose( access_t *p_access, int i_session_id )
1584 access_sys_t *p_sys = p_access->p_sys;
1585 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1587 APDUSend( p_access, i_session_id, AOT_CLOSE_MMI, NULL, 0 );
1589 p_sys->pb_slot_mmi_expected[i_slot] = true;
1592 /*****************************************************************************
1594 *****************************************************************************/
1595 static void MMIDisplayReply( access_t *p_access, int i_session_id )
1597 uint8_t p_response[2];
1599 p_response[0] = DRI_MMI_MODE_ACK;
1600 p_response[1] = MM_HIGH_LEVEL;
1602 APDUSend( p_access, i_session_id, AOT_DISPLAY_REPLY, p_response, 2 );
1604 msg_Dbg( p_access, "sending DisplayReply on session (%d)", i_session_id );
1607 /*****************************************************************************
1609 *****************************************************************************/
1610 static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size )
1612 int i_tag = APDUGetTag( *pp_apdu, *pi_size );
1616 if ( i_tag != AOT_TEXT_LAST )
1618 msg_Err( p_access, "unexpected text tag: %06x", i_tag );
1620 return strdup( "" );
1623 d = APDUGetLength( *pp_apdu, &l );
1628 return dvbsi_to_utf8((char*)d,l);
1631 /*****************************************************************************
1633 *****************************************************************************/
1634 static void MMIHandleEnq( access_t *p_access, int i_session_id,
1635 uint8_t *p_apdu, int i_size )
1637 access_sys_t *p_sys = p_access->p_sys;
1638 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1639 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1641 uint8_t *d = APDUGetLength( p_apdu, &l );
1643 en50221_MMIFree( &p_mmi->last_object );
1644 p_mmi->last_object.i_object_type = EN50221_MMI_ENQ;
1645 p_mmi->last_object.u.enq.b_blind = (*d & 0x1) ? true : false;
1646 d += 2; /* skip answer_text_length because it is not mandatory */
1648 p_mmi->last_object.u.enq.psz_text = malloc( l + 1 );
1649 strncpy( p_mmi->last_object.u.enq.psz_text, (char *)d, l );
1650 p_mmi->last_object.u.enq.psz_text[l] = '\0';
1652 msg_Dbg( p_access, "MMI enq: %s%s", p_mmi->last_object.u.enq.psz_text,
1653 p_mmi->last_object.u.enq.b_blind == true ? " (blind)" : "" );
1654 p_sys->pb_slot_mmi_expected[i_slot] = false;
1655 p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
1658 /*****************************************************************************
1660 *****************************************************************************/
1661 static void MMIHandleMenu( access_t *p_access, int i_session_id, int i_tag,
1662 uint8_t *p_apdu, int i_size )
1664 access_sys_t *p_sys = p_access->p_sys;
1665 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1666 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1668 uint8_t *d = APDUGetLength( p_apdu, &l );
1670 en50221_MMIFree( &p_mmi->last_object );
1671 p_mmi->last_object.i_object_type = (i_tag == AOT_MENU_LAST) ?
1672 EN50221_MMI_MENU : EN50221_MMI_LIST;
1673 p_mmi->last_object.u.menu.i_choices = 0;
1674 p_mmi->last_object.u.menu.ppsz_choices = NULL;
1678 l--; d++; /* choice_nb */
1680 #define GET_FIELD( x ) \
1683 p_mmi->last_object.u.menu.psz_##x \
1684 = MMIGetText( p_access, &d, &l ); \
1685 msg_Dbg( p_access, "MMI " STRINGIFY( x ) ": %s", \
1686 p_mmi->last_object.u.menu.psz_##x ); \
1690 GET_FIELD( subtitle );
1691 GET_FIELD( bottom );
1696 char *psz_text = MMIGetText( p_access, &d, &l );
1697 TAB_APPEND( p_mmi->last_object.u.menu.i_choices,
1698 p_mmi->last_object.u.menu.ppsz_choices,
1700 msg_Dbg( p_access, "MMI choice: %s", psz_text );
1703 p_sys->pb_slot_mmi_expected[i_slot] = false;
1704 p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
1707 /*****************************************************************************
1709 *****************************************************************************/
1710 static void MMIHandle( access_t *p_access, int i_session_id,
1711 uint8_t *p_apdu, int i_size )
1713 int i_tag = APDUGetTag( p_apdu, i_size );
1717 case AOT_DISPLAY_CONTROL:
1720 uint8_t *d = APDUGetLength( p_apdu, &l );
1726 case DCC_SET_MMI_MODE:
1727 if ( l == 2 && d[1] == MM_HIGH_LEVEL )
1728 MMIDisplayReply( p_access, i_session_id );
1730 msg_Err( p_access, "unsupported MMI mode %02x", d[1] );
1734 msg_Err( p_access, "unsupported display control command %02x",
1743 MMIHandleEnq( p_access, i_session_id, p_apdu, i_size );
1748 MMIHandleMenu( p_access, i_session_id, i_tag, p_apdu, i_size );
1752 SessionSendClose( p_access, i_session_id );
1756 msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
1760 /*****************************************************************************
1762 *****************************************************************************/
1763 static void MMIClose( access_t *p_access, int i_session_id )
1765 access_sys_t *p_sys = p_access->p_sys;
1766 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1767 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1769 en50221_MMIFree( &p_mmi->last_object );
1770 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1772 msg_Dbg( p_access, "closing MMI session (%d)", i_session_id );
1773 p_sys->pb_slot_mmi_expected[i_slot] = false;
1774 p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
1777 /*****************************************************************************
1779 *****************************************************************************/
1780 static void MMIOpen( access_t *p_access, int i_session_id )
1782 access_sys_t *p_sys = p_access->p_sys;
1785 msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
1787 p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
1788 p_sys->p_sessions[i_session_id - 1].pf_close = MMIClose;
1789 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(mmi_t));
1790 p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1791 p_mmi->last_object.i_object_type = EN50221_MMI_NONE;
1799 /*****************************************************************************
1800 * InitSlot: Open the transport layer
1801 *****************************************************************************/
1802 #define MAX_TC_RETRIES 20
1804 static int InitSlot( access_t * p_access, int i_slot )
1806 access_sys_t *p_sys = p_access->p_sys;
1809 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1812 msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
1814 return VLC_EGENERIC;
1817 /* This is out of the spec */
1818 for ( i = 0; i < MAX_TC_RETRIES; i++ )
1821 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
1822 && i_tag == T_CTC_REPLY )
1824 p_sys->pb_active_slot[i_slot] = true;
1828 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1832 "en50221_Init: couldn't send TPDU on slot %d",
1838 if ( p_sys->pb_active_slot[i_slot] )
1840 p_sys->i_ca_timeout = 100000;
1844 return VLC_EGENERIC;
1849 * External entry points
1852 /*****************************************************************************
1853 * en50221_Init : Initialize the CAM for en50221
1854 *****************************************************************************/
1855 int en50221_Init( access_t * p_access )
1857 access_sys_t *p_sys = p_access->p_sys;
1859 if( p_sys->i_ca_type & CA_CI_LINK )
1862 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1864 if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
1866 msg_Err( p_access, "en50221_Init: couldn't reset slot %d",
1871 p_sys->i_ca_timeout = 100000;
1872 /* Wait a bit otherwise it doesn't initialize properly... */
1879 struct ca_slot_info info;
1882 /* We don't reset the CAM in that case because it's done by the
1884 if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &info ) < 0 )
1886 msg_Err( p_access, "en50221_Init: couldn't get slot info" );
1887 close( p_sys->i_ca_handle );
1888 p_sys->i_ca_handle = 0;
1889 return VLC_EGENERIC;
1891 if( info.flags == 0 )
1893 msg_Err( p_access, "en50221_Init: no CAM inserted" );
1894 close( p_sys->i_ca_handle );
1895 p_sys->i_ca_handle = 0;
1896 return VLC_EGENERIC;
1899 /* Allocate a dummy sessions */
1900 p_sys->p_sessions[ 0 ].i_resource_id = RI_CONDITIONAL_ACCESS_SUPPORT;
1902 /* Get application info to find out which cam we are using and make
1903 sure everything is ready to play */
1906 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1907 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1908 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1909 memset( &ca_msg.msg[3], 0, 253 );
1910 APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1911 if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1913 msg_Err( p_access, "en50221_Init: failed getting message" );
1914 return VLC_EGENERIC;
1917 #if HLCI_WAIT_CAM_READY
1918 while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1920 if( !vlc_object_alive (p_access) ) return VLC_EGENERIC;
1922 msg_Dbg( p_access, "CAM: please wait" );
1923 APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1925 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1926 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1927 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1928 memset( &ca_msg.msg[3], 0, 253 );
1929 if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1931 msg_Err( p_access, "en50221_Init: failed getting message" );
1932 return VLC_EGENERIC;
1934 msg_Dbg( p_access, "en50221_Init: Got length: %d, tag: 0x%x", ca_msg.length, APDUGetTag( ca_msg.msg, ca_msg.length ) );
1937 if( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1939 msg_Err( p_access, "CAM returns garbage as application info!" );
1940 return VLC_EGENERIC;
1943 msg_Dbg( p_access, "found CAM %s using id 0x%x", &ca_msg.msg[12],
1944 (ca_msg.msg[8]<<8)|ca_msg.msg[9] );
1949 /*****************************************************************************
1950 * en50221_Poll : Poll the CAM for TPDUs
1951 *****************************************************************************/
1952 int en50221_Poll( access_t * p_access )
1954 access_sys_t *p_sys = p_access->p_sys;
1958 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1961 ca_slot_info_t sinfo;
1964 if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
1966 msg_Err( p_access, "en50221_Poll: couldn't get info on slot %d",
1971 if ( !(sinfo.flags & CA_CI_MODULE_READY) )
1973 if ( p_sys->pb_active_slot[i_slot] )
1975 msg_Dbg( p_access, "en50221_Poll: slot %d has been removed",
1977 p_sys->pb_active_slot[i_slot] = false;
1978 p_sys->pb_slot_mmi_expected[i_slot] = false;
1979 p_sys->pb_slot_mmi_undisplayed[i_slot] = false;
1981 /* Close all sessions for this slot. */
1982 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS;
1985 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1986 && p_sys->p_sessions[i_session_id - 1].i_slot
1989 if ( p_sys->p_sessions[i_session_id - 1].pf_close
1992 p_sys->p_sessions[i_session_id - 1].pf_close(
1993 p_access, i_session_id );
1995 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
2002 else if ( !p_sys->pb_active_slot[i_slot] )
2004 InitSlot( p_access, i_slot );
2006 if ( !p_sys->pb_active_slot[i_slot] )
2008 msg_Dbg( p_access, "en50221_Poll: resetting slot %d", i_slot );
2010 if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
2012 msg_Err( p_access, "en50221_Poll: couldn't reset slot %d",
2018 msg_Dbg( p_access, "en50221_Poll: slot %d is active",
2022 if ( !p_sys->pb_tc_has_data[i_slot] )
2024 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, NULL, 0 ) !=
2028 "en50221_Poll: couldn't send TPDU on slot %d",
2032 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) !=
2036 "en50221_Poll: couldn't recv TPDU on slot %d",
2042 while ( p_sys->pb_tc_has_data[i_slot] )
2044 uint8_t p_tpdu[MAX_TPDU_SIZE];
2045 int i_size, i_session_size;
2048 if ( TPDUSend( p_access, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
2051 "en50221_Poll: couldn't send TPDU on slot %d",
2055 if ( TPDURecv( p_access, i_slot, &i_tag, p_tpdu, &i_size ) !=
2059 "en50221_Poll: couldn't recv TPDU on slot %d",
2064 p_session = GetLength( &p_tpdu[3], &i_session_size );
2065 if ( i_session_size <= 1 )
2071 if ( i_tag != T_DATA_LAST )
2074 "en50221_Poll: fragmented TPDU not supported" );
2078 SPDUHandle( p_access, i_slot, p_session, i_session_size );
2082 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2084 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2085 && p_sys->p_sessions[i_session_id - 1].pf_manage )
2087 p_sys->p_sessions[i_session_id - 1].pf_manage( p_access,
2096 /*****************************************************************************
2097 * en50221_SetCAPMT :
2098 *****************************************************************************/
2099 int en50221_SetCAPMT( access_t * p_access, dvbpsi_pmt_t *p_pmt )
2101 access_sys_t *p_sys = p_access->p_sys;
2102 int i, i_session_id;
2103 bool b_update = false;
2104 bool b_needs_descrambling = CAPMTNeedsDescrambling( p_pmt );
2106 for ( i = 0; i < MAX_PROGRAMS; i++ )
2108 if ( p_sys->pp_selected_programs[i] != NULL
2109 && p_sys->pp_selected_programs[i]->i_program_number
2110 == p_pmt->i_program_number )
2114 if ( !b_needs_descrambling )
2116 dvbpsi_DeletePMT( p_pmt );
2117 p_pmt = p_sys->pp_selected_programs[i];
2118 p_sys->pp_selected_programs[i] = NULL;
2120 else if( p_pmt != p_sys->pp_selected_programs[i] )
2122 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
2123 p_sys->pp_selected_programs[i] = p_pmt;
2130 if ( !b_update && b_needs_descrambling )
2132 for ( i = 0; i < MAX_PROGRAMS; i++ )
2134 if ( p_sys->pp_selected_programs[i] == NULL )
2136 p_sys->pp_selected_programs[i] = p_pmt;
2142 if ( b_update || b_needs_descrambling )
2144 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2146 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2147 == RI_CONDITIONAL_ACCESS_SUPPORT )
2149 if ( b_update && b_needs_descrambling )
2150 CAPMTUpdate( p_access, i_session_id, p_pmt );
2151 else if ( b_update )
2152 CAPMTDelete( p_access, i_session_id, p_pmt );
2154 CAPMTAdd( p_access, i_session_id, p_pmt );
2159 if ( !b_needs_descrambling )
2161 dvbpsi_DeletePMT( p_pmt );
2167 /*****************************************************************************
2169 *****************************************************************************/
2170 int en50221_OpenMMI( access_t * p_access, int i_slot )
2172 access_sys_t *p_sys = p_access->p_sys;
2174 if( p_sys->i_ca_type & CA_CI_LINK )
2177 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2179 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2180 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2183 "MMI menu is already opened on slot %d (session=%d)",
2184 i_slot, i_session_id );
2189 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2191 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2192 == RI_APPLICATION_INFORMATION
2193 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2195 ApplicationInformationEnterMenu( p_access, i_session_id );
2200 msg_Err( p_access, "no application information on slot %d", i_slot );
2201 return VLC_EGENERIC;
2205 msg_Err( p_access, "MMI menu not supported" );
2206 return VLC_EGENERIC;
2210 /*****************************************************************************
2211 * en50221_CloseMMI :
2212 *****************************************************************************/
2213 int en50221_CloseMMI( access_t * p_access, int i_slot )
2215 access_sys_t *p_sys = p_access->p_sys;
2217 if( p_sys->i_ca_type & CA_CI_LINK )
2220 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2222 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2223 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2225 MMISendClose( p_access, i_session_id );
2230 msg_Warn( p_access, "closing a non-existing MMI session on slot %d",
2232 return VLC_EGENERIC;
2236 msg_Err( p_access, "MMI menu not supported" );
2237 return VLC_EGENERIC;
2241 /*****************************************************************************
2242 * en50221_GetMMIObject :
2243 *****************************************************************************/
2244 en50221_mmi_object_t *en50221_GetMMIObject( access_t * p_access,
2247 access_sys_t *p_sys = p_access->p_sys;
2250 if ( p_sys->pb_slot_mmi_expected[i_slot] == true )
2251 return NULL; /* should not happen */
2253 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2255 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2256 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2259 (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
2260 if ( p_mmi == NULL )
2261 return NULL; /* should not happen */
2262 return &p_mmi->last_object;
2270 /*****************************************************************************
2271 * en50221_SendMMIObject :
2272 *****************************************************************************/
2273 void en50221_SendMMIObject( access_t * p_access, int i_slot,
2274 en50221_mmi_object_t *p_object )
2276 access_sys_t *p_sys = p_access->p_sys;
2279 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2281 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2282 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2284 MMISendObject( p_access, i_session_id, p_object );
2289 msg_Err( p_access, "SendMMIObject when no MMI session is opened !" );
2292 /*****************************************************************************
2294 *****************************************************************************/
2295 void en50221_End( access_t * p_access )
2297 access_sys_t *p_sys = p_access->p_sys;
2298 int i_session_id, i;
2300 for ( i = 0; i < MAX_PROGRAMS; i++ )
2302 if ( p_sys->pp_selected_programs[i] != NULL )
2304 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
2308 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2310 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2311 && p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
2313 p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
2318 /* Leave the CAM configured, so that it can be reused in another
2322 static inline void *FixUTF8( char *p )
2328 char *dvbsi_to_utf8( char *psz_instring, size_t i_length )
2330 const char *psz_encoding, *psz_stringstart;
2331 char *psz_outstring, *psz_tmp;
2332 char psz_encbuf[12];
2334 vlc_iconv_t iconv_handle;
2335 if( i_length < 1 ) return NULL;
2336 if( psz_instring[0] < 0 || psz_instring[0] >= 0x20 )
2338 psz_stringstart = psz_instring;
2339 psz_encoding = "ISO_8859-1"; /* should be ISO6937 according to spec, but this seems to be the one used */
2340 } else switch( psz_instring[0] )
2343 psz_stringstart = &psz_instring[1];
2344 psz_encoding = "ISO_8859-5";
2347 psz_stringstart = &psz_instring[1];
2348 psz_encoding = "ISO_8859-6";
2351 psz_stringstart = &psz_instring[1];
2352 psz_encoding = "ISO_8859-7";
2355 psz_stringstart = &psz_instring[1];
2356 psz_encoding = "ISO_8859-8";
2359 psz_stringstart = &psz_instring[1];
2360 psz_encoding = "ISO_8859-9";
2363 psz_stringstart = &psz_instring[1];
2364 psz_encoding = "ISO_8859-10";
2367 psz_stringstart = &psz_instring[1];
2368 psz_encoding = "ISO_8859-11";
2371 psz_stringstart = &psz_instring[1]; /*possibly reserved?*/
2372 psz_encoding = "ISO_8859-12";
2375 psz_stringstart = &psz_instring[1];
2376 psz_encoding = "ISO_8859-13";
2379 psz_stringstart = &psz_instring[1];
2380 psz_encoding = "ISO_8859-14";
2383 psz_stringstart = &psz_instring[1];
2384 psz_encoding = "ISO_8859-15";
2387 if( i_length < 3 || psz_instring[1] != '\0' || psz_instring[2] > 0x0f
2388 || psz_instring[2] == 0 )
2389 return FixUTF8(strndup(psz_instring,i_length));
2390 sprintf( psz_encbuf, "ISO_8859-%d", psz_instring[2] );
2391 psz_stringstart = &psz_instring[3];
2392 psz_encoding = psz_encbuf;
2395 psz_stringstart = &psz_instring[1];
2396 psz_encoding = "UTF-16";
2399 psz_stringstart = &psz_instring[1];
2400 psz_encoding = "KSC5601-1987";
2403 psz_stringstart = &psz_instring[1];
2404 psz_encoding = "GB2312";/*GB-2312-1980 */
2407 psz_stringstart = &psz_instring[1];
2408 psz_encoding = "BIG-5";
2411 return FixUTF8(strndup(&psz_instring[1],i_length-1));
2415 return FixUTF8(strndup(psz_instring,i_length));
2417 iconv_handle = vlc_iconv_open( "UTF-8", psz_encoding );
2418 i_in = i_length - (psz_stringstart - psz_instring );
2420 psz_outstring = psz_tmp = (char*)malloc( i_out * sizeof(char) + 1 );
2421 vlc_iconv( iconv_handle, &psz_stringstart, &i_in, &psz_tmp, &i_out );
2422 vlc_iconv_close( iconv_handle );
2424 return psz_outstring;