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 *****************************************************************************/
26 #include <vlc_access.h>
28 #include <sys/ioctl.h>
31 #include <sys/types.h>
39 /* DVB Card Drivers */
40 #include <linux/dvb/version.h>
41 #include <linux/dvb/dmx.h>
42 #include <linux/dvb/frontend.h>
43 #include <linux/dvb/ca.h>
45 /* Include dvbpsi headers */
46 #ifdef HAVE_DVBPSI_DR_H
47 # include <dvbpsi/dvbpsi.h>
48 # include <dvbpsi/descriptor.h>
49 # include <dvbpsi/pat.h>
50 # include <dvbpsi/pmt.h>
51 # include <dvbpsi/dr.h>
52 # include <dvbpsi/psi.h>
55 # include "descriptor.h"
56 # include "tables/pat.h"
57 # include "tables/pmt.h"
58 # include "descriptors/dr.h"
63 # include "vlc_httpd.h"
68 #include <vlc_charset.h>
71 #define HLCI_WAIT_CAM_READY 0
72 #define CAM_PROG_MAX MAX_PROGRAMS
74 static void ResourceManagerOpen( access_t * p_access, int i_session_id );
75 static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
76 static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
77 static void DateTimeOpen( access_t * p_access, int i_session_id );
78 static void MMIOpen( access_t * p_access, int i_session_id );
79 static char *dvbsi_to_utf8( char *psz_instring, size_t i_length );
81 /*****************************************************************************
83 *****************************************************************************/
84 #define SIZE_INDICATOR 0x80
86 static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
88 *pi_length = *p_data++;
90 if ( (*pi_length & SIZE_INDICATOR) != 0 )
92 int l = *pi_length & ~SIZE_INDICATOR;
96 for ( i = 0; i < l; i++ )
97 *pi_length = (*pi_length << 8) | *p_data++;
103 static uint8_t *SetLength( uint8_t *p_data, int i_length )
107 if ( i_length < 128 )
111 else if ( i_length < 256 )
113 *p++ = SIZE_INDICATOR | 0x1;
116 else if ( i_length < 65536 )
118 *p++ = SIZE_INDICATOR | 0x2;
119 *p++ = i_length >> 8;
120 *p++ = i_length & 0xff;
122 else if ( i_length < 16777216 )
124 *p++ = SIZE_INDICATOR | 0x3;
125 *p++ = i_length >> 16;
126 *p++ = (i_length >> 8) & 0xff;
127 *p++ = i_length & 0xff;
131 *p++ = SIZE_INDICATOR | 0x4;
132 *p++ = i_length >> 24;
133 *p++ = (i_length >> 16) & 0xff;
134 *p++ = (i_length >> 8) & 0xff;
135 *p++ = i_length & 0xff;
146 #define MAX_TPDU_SIZE 2048
147 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
149 #define DATA_INDICATOR 0x80
153 #define T_CREATE_TC 0x82
154 #define T_CTC_REPLY 0x83
155 #define T_DELETE_TC 0x84
156 #define T_DTC_REPLY 0x85
157 #define T_REQUEST_TC 0x86
158 #define T_NEW_TC 0x87
159 #define T_TC_ERROR 0x88
160 #define T_DATA_LAST 0xA0
161 #define T_DATA_MORE 0xA1
163 static void Dump( vlc_bool_t b_outgoing, uint8_t *p_data, int i_size )
168 fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
169 for ( i = 0; i < i_size && i < MAX_DUMP; i++)
170 fprintf(stderr, "%02X ", p_data[i]);
171 fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
175 /*****************************************************************************
177 *****************************************************************************/
178 static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
179 const uint8_t *p_content, int i_length )
181 access_sys_t *p_sys = p_access->p_sys;
182 uint8_t i_tcid = i_slot + 1;
183 uint8_t p_data[MAX_TPDU_SIZE];
199 p_data[3] = 1; /* length */
206 p_data[3] = 2; /* length */
208 p_data[5] = p_content[0];
215 /* i_length <= MAX_TPDU_DATA */
216 uint8_t *p = p_data + 3;
217 p = SetLength( p, i_length + 1 );
221 memcpy( p, p_content, i_length );
222 i_size = i_length + (p - p_data);
229 Dump( VLC_TRUE, p_data, i_size );
231 if ( write( p_sys->i_ca_handle, p_data, i_size ) != i_size )
233 msg_Err( p_access, "cannot write to CAM device (%m)" );
241 /*****************************************************************************
243 *****************************************************************************/
244 #define CAM_READ_TIMEOUT 3500 // ms
246 static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
247 uint8_t *p_data, int *pi_size )
249 access_sys_t *p_sys = p_access->p_sys;
250 uint8_t i_tcid = i_slot + 1;
252 struct pollfd pfd[1];
254 pfd[0].fd = p_sys->i_ca_handle;
255 pfd[0].events = POLLIN;
256 if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
258 msg_Err( p_access, "cannot poll from CAM device" );
262 if ( pi_size == NULL )
264 p_data = malloc( MAX_TPDU_SIZE );
269 i_size = read( p_sys->i_ca_handle, p_data, MAX_TPDU_SIZE );
271 if ( i_size >= 0 || errno != EINTR )
277 msg_Err( p_access, "cannot read from CAM device (%d:%m)", i_size );
281 if ( p_data[1] != i_tcid )
283 msg_Err( p_access, "invalid read from CAM device (%d instead of %d)",
289 p_sys->pb_tc_has_data[i_slot] = (i_size >= 4
290 && p_data[i_size - 4] == T_SB
291 && p_data[i_size - 3] == 2
292 && (p_data[i_size - 1] & DATA_INDICATOR))
293 ? VLC_TRUE : VLC_FALSE;
295 Dump( VLC_FALSE, p_data, i_size );
297 if ( pi_size == NULL )
310 #define ST_SESSION_NUMBER 0x90
311 #define ST_OPEN_SESSION_REQUEST 0x91
312 #define ST_OPEN_SESSION_RESPONSE 0x92
313 #define ST_CREATE_SESSION 0x93
314 #define ST_CREATE_SESSION_RESPONSE 0x94
315 #define ST_CLOSE_SESSION_REQUEST 0x95
316 #define ST_CLOSE_SESSION_RESPONSE 0x96
319 #define SS_NOT_ALLOCATED 0xF0
321 #define RI_RESOURCE_MANAGER 0x00010041
322 #define RI_APPLICATION_INFORMATION 0x00020041
323 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
324 #define RI_HOST_CONTROL 0x00200041
325 #define RI_DATE_TIME 0x00240041
326 #define RI_MMI 0x00400041
328 static int ResourceIdToInt( uint8_t *p_data )
330 return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
331 | ((int)p_data[2] << 8) | p_data[3];
334 /*****************************************************************************
336 *****************************************************************************/
337 static int SPDUSend( access_t * p_access, int i_session_id,
338 uint8_t *p_data, int i_size )
340 access_sys_t *p_sys = p_access->p_sys;
341 uint8_t *p_spdu = malloc( i_size + 4 );
344 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
346 *p++ = ST_SESSION_NUMBER;
348 *p++ = (i_session_id >> 8);
349 *p++ = i_session_id & 0xff;
351 memcpy( p, p_data, i_size );
358 if ( i_size > MAX_TPDU_DATA )
360 if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
361 MAX_TPDU_DATA ) != VLC_SUCCESS )
363 msg_Err( p_access, "couldn't send TPDU on session %d",
369 i_size -= MAX_TPDU_DATA;
373 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
376 msg_Err( p_access, "couldn't send TPDU on session %d",
384 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
387 msg_Err( p_access, "couldn't recv TPDU on session %d",
398 /*****************************************************************************
400 *****************************************************************************/
401 static void SessionOpen( access_t * p_access, uint8_t i_slot,
402 uint8_t *p_spdu, int i_size )
404 access_sys_t *p_sys = p_access->p_sys;
406 int i_resource_id = ResourceIdToInt( &p_spdu[2] );
407 uint8_t p_response[16];
408 int i_status = SS_NOT_ALLOCATED;
411 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
413 if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
416 if ( i_session_id == MAX_SESSIONS )
418 msg_Err( p_access, "too many sessions !" );
421 p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
422 p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
423 p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
424 p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
426 if ( i_resource_id == RI_RESOURCE_MANAGER
427 || i_resource_id == RI_APPLICATION_INFORMATION
428 || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
429 || i_resource_id == RI_DATE_TIME
430 || i_resource_id == RI_MMI )
435 p_response[0] = ST_OPEN_SESSION_RESPONSE;
437 p_response[2] = i_status;
438 p_response[3] = p_spdu[2];
439 p_response[4] = p_spdu[3];
440 p_response[5] = p_spdu[4];
441 p_response[6] = p_spdu[5];
442 p_response[7] = i_session_id >> 8;
443 p_response[8] = i_session_id & 0xff;
445 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) !=
449 "SessionOpen: couldn't send TPDU on slot %d", i_slot );
452 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
455 "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
459 switch ( i_resource_id )
461 case RI_RESOURCE_MANAGER:
462 ResourceManagerOpen( p_access, i_session_id ); break;
463 case RI_APPLICATION_INFORMATION:
464 ApplicationInformationOpen( p_access, i_session_id ); break;
465 case RI_CONDITIONAL_ACCESS_SUPPORT:
466 ConditionalAccessOpen( p_access, i_session_id ); break;
468 DateTimeOpen( p_access, i_session_id ); break;
470 MMIOpen( p_access, i_session_id ); break;
472 case RI_HOST_CONTROL:
474 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
475 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
480 /* unused code for the moment - commented out to keep gcc happy */
481 /*****************************************************************************
483 *****************************************************************************/
484 static void SessionCreate( access_t * p_access, int i_slot, int i_resource_id )
486 access_sys_t *p_sys = p_access->p_sys;
487 uint8_t p_response[16];
491 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
493 if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
496 if ( i_session_id == MAX_SESSIONS )
498 msg_Err( p_access, "too many sessions !" );
501 p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
502 p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
503 p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
504 p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
505 p_sys->p_sessions[i_session_id - 1].p_sys = NULL;
507 p_response[0] = ST_CREATE_SESSION;
509 p_response[2] = i_resource_id >> 24;
510 p_response[3] = (i_resource_id >> 16) & 0xff;
511 p_response[4] = (i_resource_id >> 8) & 0xff;
512 p_response[5] = i_resource_id & 0xff;
513 p_response[6] = i_session_id >> 8;
514 p_response[7] = i_session_id & 0xff;
516 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
520 "SessionCreate: couldn't send TPDU on slot %d", i_slot );
523 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
526 "SessionCreate: couldn't recv TPDU on slot %d", i_slot );
532 /*****************************************************************************
533 * SessionCreateResponse
534 *****************************************************************************/
535 static void SessionCreateResponse( access_t * p_access, uint8_t i_slot,
536 uint8_t *p_spdu, int i_size )
538 access_sys_t *p_sys = p_access->p_sys;
539 int i_status = p_spdu[2];
540 int i_resource_id = ResourceIdToInt( &p_spdu[3] );
541 int i_session_id = ((int)p_spdu[7] << 8) | p_spdu[8];
543 if ( i_status != SS_OK )
545 msg_Err( p_access, "SessionCreateResponse: failed to open session %d"
546 " resource=0x%x status=0x%x", i_session_id, i_resource_id,
548 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
552 switch ( i_resource_id )
554 case RI_RESOURCE_MANAGER:
555 ResourceManagerOpen( p_access, i_session_id ); break;
556 case RI_APPLICATION_INFORMATION:
557 ApplicationInformationOpen( p_access, i_session_id ); break;
558 case RI_CONDITIONAL_ACCESS_SUPPORT:
559 ConditionalAccessOpen( p_access, i_session_id ); break;
561 DateTimeOpen( p_access, i_session_id ); break;
563 MMIOpen( p_access, i_session_id ); break;
565 case RI_HOST_CONTROL:
567 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
568 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
572 /*****************************************************************************
574 *****************************************************************************/
575 static void SessionSendClose( access_t * p_access, int i_session_id )
577 access_sys_t *p_sys = p_access->p_sys;
578 uint8_t p_response[16];
580 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
582 p_response[0] = ST_CLOSE_SESSION_REQUEST;
584 p_response[2] = i_session_id >> 8;
585 p_response[3] = i_session_id & 0xff;
587 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
591 "SessionSendClose: couldn't send TPDU on slot %d", i_slot );
594 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
597 "SessionSendClose: couldn't recv TPDU on slot %d", i_slot );
602 /*****************************************************************************
604 *****************************************************************************/
605 static void SessionClose( access_t * p_access, int i_session_id )
607 access_sys_t *p_sys = p_access->p_sys;
608 uint8_t p_response[16];
610 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
612 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
613 p_sys->p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
614 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
616 p_response[0] = ST_CLOSE_SESSION_RESPONSE;
618 p_response[2] = SS_OK;
619 p_response[3] = i_session_id >> 8;
620 p_response[4] = i_session_id & 0xff;
622 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
626 "SessionClose: couldn't send TPDU on slot %d", i_slot );
629 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
632 "SessionClose: couldn't recv TPDU on slot %d", i_slot );
637 /*****************************************************************************
639 *****************************************************************************/
640 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
641 uint8_t *p_spdu, int i_size )
643 access_sys_t *p_sys = p_access->p_sys;
648 case ST_SESSION_NUMBER:
651 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
652 p_sys->p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
653 p_spdu + 4, i_size - 4 );
656 case ST_OPEN_SESSION_REQUEST:
657 if ( i_size != 6 || p_spdu[1] != 0x4 )
659 SessionOpen( p_access, i_slot, p_spdu, i_size );
662 case ST_CREATE_SESSION_RESPONSE:
663 if ( i_size != 9 || p_spdu[1] != 0x7 )
665 SessionCreateResponse( p_access, i_slot, p_spdu, i_size );
668 case ST_CLOSE_SESSION_REQUEST:
669 if ( i_size != 4 || p_spdu[1] != 0x2 )
671 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
672 SessionClose( p_access, i_session_id );
675 case ST_CLOSE_SESSION_RESPONSE:
676 if ( i_size != 5 || p_spdu[1] != 0x3 )
678 i_session_id = ((int)p_spdu[3] << 8) | p_spdu[4];
681 msg_Err( p_access, "closing a session which is not allocated (%d)",
686 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
687 p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
689 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
694 msg_Err( p_access, "unexpected tag in SPDUHandle (%x)", p_spdu[0] );
704 #define AOT_NONE 0x000000
705 #define AOT_PROFILE_ENQ 0x9F8010
706 #define AOT_PROFILE 0x9F8011
707 #define AOT_PROFILE_CHANGE 0x9F8012
708 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
709 #define AOT_APPLICATION_INFO 0x9F8021
710 #define AOT_ENTER_MENU 0x9F8022
711 #define AOT_CA_INFO_ENQ 0x9F8030
712 #define AOT_CA_INFO 0x9F8031
713 #define AOT_CA_PMT 0x9F8032
714 #define AOT_CA_PMT_REPLY 0x9F8033
715 #define AOT_TUNE 0x9F8400
716 #define AOT_REPLACE 0x9F8401
717 #define AOT_CLEAR_REPLACE 0x9F8402
718 #define AOT_ASK_RELEASE 0x9F8403
719 #define AOT_DATE_TIME_ENQ 0x9F8440
720 #define AOT_DATE_TIME 0x9F8441
721 #define AOT_CLOSE_MMI 0x9F8800
722 #define AOT_DISPLAY_CONTROL 0x9F8801
723 #define AOT_DISPLAY_REPLY 0x9F8802
724 #define AOT_TEXT_LAST 0x9F8803
725 #define AOT_TEXT_MORE 0x9F8804
726 #define AOT_KEYPAD_CONTROL 0x9F8805
727 #define AOT_KEYPRESS 0x9F8806
728 #define AOT_ENQ 0x9F8807
729 #define AOT_ANSW 0x9F8808
730 #define AOT_MENU_LAST 0x9F8809
731 #define AOT_MENU_MORE 0x9F880A
732 #define AOT_MENU_ANSW 0x9F880B
733 #define AOT_LIST_LAST 0x9F880C
734 #define AOT_LIST_MORE 0x9F880D
735 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
736 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
737 #define AOT_DISPLAY_MESSAGE 0x9F8810
738 #define AOT_SCENE_END_MARK 0x9F8811
739 #define AOT_SCENE_DONE 0x9F8812
740 #define AOT_SCENE_CONTROL 0x9F8813
741 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
742 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
743 #define AOT_FLUSH_DOWNLOAD 0x9F8816
744 #define AOT_DOWNLOAD_REPLY 0x9F8817
745 #define AOT_COMMS_CMD 0x9F8C00
746 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
747 #define AOT_COMMS_REPLY 0x9F8C02
748 #define AOT_COMMS_SEND_LAST 0x9F8C03
749 #define AOT_COMMS_SEND_MORE 0x9F8C04
750 #define AOT_COMMS_RCV_LAST 0x9F8C05
751 #define AOT_COMMS_RCV_MORE 0x9F8C06
753 /*****************************************************************************
755 *****************************************************************************/
756 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
761 for ( i = 0; i < 3; i++ )
762 t = (t << 8) | *p_apdu++;
769 /*****************************************************************************
771 *****************************************************************************/
772 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
774 return GetLength( &p_apdu[3], pi_size );
777 /*****************************************************************************
779 *****************************************************************************/
780 static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
781 uint8_t *p_data, int i_size )
783 access_sys_t *p_sys = p_access->p_sys;
784 uint8_t *p_apdu = malloc( i_size + 12 );
789 *p++ = (i_tag >> 16);
790 *p++ = (i_tag >> 8) & 0xff;
792 p = SetLength( p, i_size );
794 memcpy( p, p_data, i_size );
795 if ( p_sys->i_ca_type == CA_CI_LINK )
797 i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
801 if ( i_size + p - p_apdu > 256 )
803 msg_Err( p_access, "CAM: apdu overflow" );
804 i_ret = VLC_EGENERIC;
809 ca_msg.length = i_size + p - p_apdu;
810 if ( i_size == 0 ) ca_msg.length=3;
811 psz_hex = (char*)malloc( ca_msg.length*3 + 1);
812 memcpy( ca_msg.msg, p_apdu, i_size + p - p_apdu );
813 i_ret = ioctl(p_sys->i_ca_handle, CA_SEND_MSG, &ca_msg );
816 msg_Err( p_access, "Error sending to CAM: %m" );
817 i_ret = VLC_EGENERIC;
829 /*****************************************************************************
830 * ResourceManagerHandle
831 *****************************************************************************/
832 static void ResourceManagerHandle( access_t * p_access, int i_session_id,
833 uint8_t *p_apdu, int i_size )
835 int i_tag = APDUGetTag( p_apdu, i_size );
839 case AOT_PROFILE_ENQ:
841 int resources[] = { htonl(RI_RESOURCE_MANAGER),
842 htonl(RI_APPLICATION_INFORMATION),
843 htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
847 APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
852 APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
856 msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
861 /*****************************************************************************
862 * ResourceManagerOpen
863 *****************************************************************************/
864 static void ResourceManagerOpen( access_t * p_access, int i_session_id )
866 access_sys_t *p_sys = p_access->p_sys;
868 msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
870 p_sys->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
872 APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
876 * Application Information
879 /*****************************************************************************
880 * ApplicationInformationEnterMenu
881 *****************************************************************************/
882 static void ApplicationInformationEnterMenu( access_t * p_access,
885 access_sys_t *p_sys = p_access->p_sys;
886 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
888 msg_Dbg( p_access, "entering MMI menus on session %d", i_session_id );
889 APDUSend( p_access, i_session_id, AOT_ENTER_MENU, NULL, 0 );
890 p_sys->pb_slot_mmi_expected[i_slot] = VLC_TRUE;
893 /*****************************************************************************
894 * ApplicationInformationHandle
895 *****************************************************************************/
896 static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
897 uint8_t *p_apdu, int i_size )
899 int i_tag = APDUGetTag( p_apdu, i_size );
903 case AOT_APPLICATION_INFO:
905 int i_type, i_manufacturer, i_code;
907 uint8_t *d = APDUGetLength( p_apdu, &l );
910 p_apdu[l + 4] = '\0';
913 i_manufacturer = ((int)d[0] << 8) | d[1];
915 i_code = ((int)d[0] << 8) | d[1];
917 d = GetLength( d, &l );
919 msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
920 d, i_type, i_manufacturer, i_code );
925 "unexpected tag in ApplicationInformationHandle (0x%x)",
930 /*****************************************************************************
931 * ApplicationInformationOpen
932 *****************************************************************************/
933 static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
935 access_sys_t *p_sys = p_access->p_sys;
937 msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
939 p_sys->p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
941 APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
948 #define MAX_CASYSTEM_IDS 16
952 uint16_t pi_system_ids[MAX_CASYSTEM_IDS + 1];
955 static vlc_bool_t CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
958 if( !p_ids ) return VLC_TRUE;
960 while ( p_ids->pi_system_ids[i] )
962 if ( p_ids->pi_system_ids[i] == i_id )
970 /*****************************************************************************
971 * CAPMTNeedsDescrambling
972 *****************************************************************************/
973 static vlc_bool_t CAPMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
975 dvbpsi_descriptor_t *p_dr;
976 dvbpsi_pmt_es_t *p_es;
978 for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
980 if( p_dr->i_tag == 0x9 )
986 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
988 for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
989 p_dr = p_dr->p_next )
991 if( p_dr->i_tag == 0x9 )
1001 /*****************************************************************************
1003 *****************************************************************************/
1004 static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr )
1008 while ( p_dr != NULL )
1010 if( p_dr->i_tag == 0x9 )
1012 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1014 if ( CheckSystemID( p_ids, i_sysid ) )
1015 i_cad_size += p_dr->i_length + 2;
1017 p_dr = p_dr->p_next;
1023 static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt,
1024 uint16_t i_program_number, uint8_t i_version,
1025 int i_size, dvbpsi_descriptor_t *p_dr,
1031 p_data = malloc( 7 + i_size );
1033 p_data = malloc( 6 );
1035 p_data[0] = i_list_mgt;
1036 p_data[1] = i_program_number >> 8;
1037 p_data[2] = i_program_number & 0xff;
1038 p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
1044 p_data[4] = (i_size + 1) >> 8;
1045 p_data[5] = (i_size + 1) & 0xff;
1049 while ( p_dr != NULL )
1051 if( p_dr->i_tag == 0x9 )
1053 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1055 if ( CheckSystemID( p_ids, i_sysid ) )
1058 p_data[i + 1] = p_dr->i_length;
1059 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1060 // p_data[i+4] &= 0x1f;
1061 i += p_dr->i_length + 2;
1064 p_dr = p_dr->p_next;
1076 static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt,
1077 int i_capmt_size, uint8_t i_type, uint16_t i_pid,
1078 int i_size, dvbpsi_descriptor_t *p_dr,
1085 p_data = realloc( p_capmt, i_capmt_size + 6 + i_size );
1087 p_data = realloc( p_capmt, i_capmt_size + 5 );
1092 p_data[i + 1] = i_pid >> 8;
1093 p_data[i + 2] = i_pid & 0xff;
1097 p_data[i + 3] = (i_size + 1) >> 8;
1098 p_data[i + 4] = (i_size + 1) & 0xff;
1099 p_data[i + 5] = i_cmd;
1102 while ( p_dr != NULL )
1104 if( p_dr->i_tag == 0x9 )
1106 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1108 if ( CheckSystemID( p_ids, i_sysid ) )
1111 p_data[i + 1] = p_dr->i_length;
1112 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1113 i += p_dr->i_length + 2;
1116 p_dr = p_dr->p_next;
1128 static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id,
1129 dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt,
1130 uint8_t i_cmd, int *pi_capmt_size )
1132 access_sys_t *p_sys = p_access->p_sys;
1133 system_ids_t *p_ids =
1134 (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1135 dvbpsi_pmt_es_t *p_es;
1136 int i_cad_size, i_cad_program_size;
1139 i_cad_size = i_cad_program_size =
1140 GetCADSize( p_ids, p_pmt->p_first_descriptor );
1141 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1143 i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor );
1149 "no compatible scrambling system for SID %d on session %d",
1150 p_pmt->i_program_number, i_session_id );
1155 p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number,
1156 p_pmt->i_version, i_cad_program_size,
1157 p_pmt->p_first_descriptor, i_cmd );
1159 if ( i_cad_program_size )
1160 *pi_capmt_size = 7 + i_cad_program_size;
1164 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1166 i_cad_size = GetCADSize( p_ids, p_es->p_first_descriptor );
1168 if ( i_cad_size || i_cad_program_size )
1170 p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type,
1171 p_es->i_pid, i_cad_size,
1172 p_es->p_first_descriptor, i_cmd );
1174 *pi_capmt_size += 6 + i_cad_size;
1176 *pi_capmt_size += 5;
1183 /*****************************************************************************
1185 *****************************************************************************/
1186 static void CAPMTFirst( access_t * p_access, int i_session_id,
1187 dvbpsi_pmt_t *p_pmt )
1192 msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d",
1193 p_pmt->i_program_number, i_session_id );
1195 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1196 0x3 /* only */, 0x1 /* ok_descrambling */,
1200 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1203 /*****************************************************************************
1205 *****************************************************************************/
1206 static void CAPMTAdd( access_t * p_access, int i_session_id,
1207 dvbpsi_pmt_t *p_pmt )
1212 if( p_access->p_sys->i_selected_programs >= CAM_PROG_MAX )
1214 msg_Warn( p_access, "Not adding CAPMT for SID %d, too many programs",
1215 p_pmt->i_program_number );
1218 p_access->p_sys->i_selected_programs++;
1219 if( p_access->p_sys->i_selected_programs == 1 )
1221 CAPMTFirst( p_access, i_session_id, p_pmt );
1226 msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
1227 p_pmt->i_program_number, i_session_id );
1229 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1230 0x4 /* add */, 0x1 /* ok_descrambling */,
1234 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1237 /*****************************************************************************
1239 *****************************************************************************/
1240 static void CAPMTUpdate( access_t * p_access, int i_session_id,
1241 dvbpsi_pmt_t *p_pmt )
1246 msg_Dbg( p_access, "updating CAPMT for SID %d on session %d",
1247 p_pmt->i_program_number, i_session_id );
1249 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1250 0x5 /* update */, 0x1 /* ok_descrambling */,
1254 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1257 /*****************************************************************************
1259 *****************************************************************************/
1260 static void CAPMTDelete( access_t * p_access, int i_session_id,
1261 dvbpsi_pmt_t *p_pmt )
1266 p_access->p_sys->i_selected_programs--;
1267 msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d",
1268 p_pmt->i_program_number, i_session_id );
1270 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1271 0x5 /* update */, 0x4 /* not selected */,
1275 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1278 /*****************************************************************************
1279 * ConditionalAccessHandle
1280 *****************************************************************************/
1281 static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
1282 uint8_t *p_apdu, int i_size )
1284 access_sys_t *p_sys = p_access->p_sys;
1285 system_ids_t *p_ids =
1286 (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1287 int i_tag = APDUGetTag( p_apdu, i_size );
1295 uint8_t *d = APDUGetLength( p_apdu, &l );
1296 msg_Dbg( p_access, "CA system IDs supported by the application :" );
1298 for ( i = 0; i < l / 2; i++ )
1300 p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
1302 msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] );
1304 p_ids->pi_system_ids[i] = 0;
1306 for ( i = 0; i < MAX_PROGRAMS; i++ )
1308 if ( p_sys->pp_selected_programs[i] != NULL )
1310 CAPMTAdd( p_access, i_session_id,
1311 p_sys->pp_selected_programs[i] );
1319 "unexpected tag in ConditionalAccessHandle (0x%x)",
1324 /*****************************************************************************
1325 * ConditionalAccessClose
1326 *****************************************************************************/
1327 static void ConditionalAccessClose( access_t * p_access, int i_session_id )
1329 access_sys_t *p_sys = p_access->p_sys;
1331 msg_Dbg( p_access, "closing ConditionalAccess session (%d)", i_session_id );
1333 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1336 /*****************************************************************************
1337 * ConditionalAccessOpen
1338 *****************************************************************************/
1339 static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
1341 access_sys_t *p_sys = p_access->p_sys;
1343 msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
1345 p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
1346 p_sys->p_sessions[i_session_id - 1].pf_close = ConditionalAccessClose;
1347 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(system_ids_t));
1348 memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0,
1349 sizeof(system_ids_t) );
1351 APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
1364 /*****************************************************************************
1366 *****************************************************************************/
1367 static void DateTimeSend( access_t * p_access, int i_session_id )
1369 access_sys_t *p_sys = p_access->p_sys;
1370 date_time_t *p_date =
1371 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1373 time_t t = time(NULL);
1377 if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
1379 int Y = tm_gmt.tm_year;
1380 int M = tm_gmt.tm_mon + 1;
1381 int D = tm_gmt.tm_mday;
1382 int L = (M == 1 || M == 2) ? 1 : 0;
1383 int MJD = 14956 + D + (int)((Y - L) * 365.25)
1384 + (int)((M + 1 + L * 12) * 30.6001);
1385 uint8_t p_response[7];
1387 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
1389 p_response[0] = htons(MJD) >> 8;
1390 p_response[1] = htons(MJD) & 0xff;
1391 p_response[2] = DEC2BCD(tm_gmt.tm_hour);
1392 p_response[3] = DEC2BCD(tm_gmt.tm_min);
1393 p_response[4] = DEC2BCD(tm_gmt.tm_sec);
1394 p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
1395 p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
1397 APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
1399 p_date->i_last = mdate();
1403 /*****************************************************************************
1405 *****************************************************************************/
1406 static void DateTimeHandle( access_t * p_access, int i_session_id,
1407 uint8_t *p_apdu, int i_size )
1409 access_sys_t *p_sys = p_access->p_sys;
1410 date_time_t *p_date =
1411 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1413 int i_tag = APDUGetTag( p_apdu, i_size );
1417 case AOT_DATE_TIME_ENQ:
1420 const uint8_t *d = APDUGetLength( p_apdu, &l );
1424 p_date->i_interval = *d;
1425 msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
1426 p_date->i_interval );
1429 p_date->i_interval = 0;
1431 DateTimeSend( p_access, i_session_id );
1435 msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
1439 /*****************************************************************************
1441 *****************************************************************************/
1442 static void DateTimeManage( access_t * p_access, int i_session_id )
1444 access_sys_t *p_sys = p_access->p_sys;
1445 date_time_t *p_date =
1446 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1448 if ( p_date->i_interval
1449 && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
1451 DateTimeSend( p_access, i_session_id );
1455 /*****************************************************************************
1457 *****************************************************************************/
1458 static void DateTimeClose( access_t * p_access, int i_session_id )
1460 access_sys_t *p_sys = p_access->p_sys;
1462 msg_Dbg( p_access, "closing DateTime session (%d)", i_session_id );
1464 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1467 /*****************************************************************************
1469 *****************************************************************************/
1470 static void DateTimeOpen( access_t * p_access, int i_session_id )
1472 access_sys_t *p_sys = p_access->p_sys;
1474 msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
1476 p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
1477 p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
1478 p_sys->p_sessions[i_session_id - 1].pf_close = DateTimeClose;
1479 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t));
1480 memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
1482 DateTimeSend( p_access, i_session_id );
1489 /* Display Control Commands */
1491 #define DCC_SET_MMI_MODE 0x01
1492 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
1493 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
1494 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
1495 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
1499 #define MM_HIGH_LEVEL 0x01
1500 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
1501 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
1503 /* Display Reply IDs */
1505 #define DRI_MMI_MODE_ACK 0x01
1506 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
1507 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
1508 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
1509 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
1510 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
1511 #define DRI_UNKNOWN_MMI_MODE 0xF1
1512 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
1516 #define EF_BLIND 0x01
1520 #define AI_CANCEL 0x00
1521 #define AI_ANSWER 0x01
1525 en50221_mmi_object_t last_object;
1528 /*****************************************************************************
1530 *****************************************************************************/
1531 static void MMISendObject( access_t *p_access, int i_session_id,
1532 en50221_mmi_object_t *p_object )
1534 access_sys_t *p_sys = p_access->p_sys;
1535 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1539 switch ( p_object->i_object_type )
1541 case EN50221_MMI_ANSW:
1543 i_size = 1 + strlen( p_object->u.answ.psz_answ );
1544 p_data = malloc( i_size );
1545 p_data[0] = (p_object->u.answ.b_ok == VLC_TRUE) ? 0x1 : 0x0;
1546 strncpy( (char *)&p_data[1], p_object->u.answ.psz_answ, i_size - 1 );
1549 case EN50221_MMI_MENU_ANSW:
1550 i_tag = AOT_MENU_ANSW;
1552 p_data = malloc( i_size );
1553 p_data[0] = p_object->u.menu_answ.i_choice;
1557 msg_Err( p_access, "unknown MMI object %d", p_object->i_object_type );
1561 APDUSend( p_access, i_session_id, i_tag, p_data, i_size );
1564 p_sys->pb_slot_mmi_expected[i_slot] = VLC_TRUE;
1567 /*****************************************************************************
1569 *****************************************************************************/
1570 static void MMISendClose( access_t *p_access, int i_session_id )
1572 access_sys_t *p_sys = p_access->p_sys;
1573 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1575 APDUSend( p_access, i_session_id, AOT_CLOSE_MMI, NULL, 0 );
1577 p_sys->pb_slot_mmi_expected[i_slot] = VLC_TRUE;
1580 /*****************************************************************************
1582 *****************************************************************************/
1583 static void MMIDisplayReply( access_t *p_access, int i_session_id )
1585 uint8_t p_response[2];
1587 p_response[0] = DRI_MMI_MODE_ACK;
1588 p_response[1] = MM_HIGH_LEVEL;
1590 APDUSend( p_access, i_session_id, AOT_DISPLAY_REPLY, p_response, 2 );
1592 msg_Dbg( p_access, "sending DisplayReply on session (%d)", i_session_id );
1595 /*****************************************************************************
1597 *****************************************************************************/
1598 static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size )
1600 int i_tag = APDUGetTag( *pp_apdu, *pi_size );
1604 if ( i_tag != AOT_TEXT_LAST )
1606 msg_Err( p_access, "unexpected text tag: %06x", i_tag );
1608 return strdup( "" );
1611 d = APDUGetLength( *pp_apdu, &l );
1616 return dvbsi_to_utf8((char*)d,l);
1619 /*****************************************************************************
1621 *****************************************************************************/
1622 static void MMIHandleEnq( access_t *p_access, int i_session_id,
1623 uint8_t *p_apdu, int i_size )
1625 access_sys_t *p_sys = p_access->p_sys;
1626 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1627 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1629 uint8_t *d = APDUGetLength( p_apdu, &l );
1631 en50221_MMIFree( &p_mmi->last_object );
1632 p_mmi->last_object.i_object_type = EN50221_MMI_ENQ;
1633 p_mmi->last_object.u.enq.b_blind = (*d & 0x1) ? VLC_TRUE : VLC_FALSE;
1634 d += 2; /* skip answer_text_length because it is not mandatory */
1636 p_mmi->last_object.u.enq.psz_text = malloc( l + 1 );
1637 strncpy( p_mmi->last_object.u.enq.psz_text, (char *)d, l );
1638 p_mmi->last_object.u.enq.psz_text[l] = '\0';
1640 msg_Dbg( p_access, "MMI enq: %s%s", p_mmi->last_object.u.enq.psz_text,
1641 p_mmi->last_object.u.enq.b_blind == VLC_TRUE ? " (blind)" : "" );
1642 p_sys->pb_slot_mmi_expected[i_slot] = VLC_FALSE;
1643 p_sys->pb_slot_mmi_undisplayed[i_slot] = VLC_TRUE;
1646 /*****************************************************************************
1648 *****************************************************************************/
1649 static void MMIHandleMenu( access_t *p_access, int i_session_id, int i_tag,
1650 uint8_t *p_apdu, int i_size )
1652 access_sys_t *p_sys = p_access->p_sys;
1653 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1654 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1656 uint8_t *d = APDUGetLength( p_apdu, &l );
1658 en50221_MMIFree( &p_mmi->last_object );
1659 p_mmi->last_object.i_object_type = (i_tag == AOT_MENU_LAST) ?
1660 EN50221_MMI_MENU : EN50221_MMI_LIST;
1661 p_mmi->last_object.u.menu.i_choices = 0;
1662 p_mmi->last_object.u.menu.ppsz_choices = NULL;
1666 l--; d++; /* choice_nb */
1668 #define GET_FIELD( x ) \
1671 p_mmi->last_object.u.menu.psz_##x \
1672 = MMIGetText( p_access, &d, &l ); \
1673 msg_Dbg( p_access, "MMI " STRINGIFY( x ) ": %s", \
1674 p_mmi->last_object.u.menu.psz_##x ); \
1678 GET_FIELD( subtitle );
1679 GET_FIELD( bottom );
1684 char *psz_text = MMIGetText( p_access, &d, &l );
1685 TAB_APPEND( p_mmi->last_object.u.menu.i_choices,
1686 p_mmi->last_object.u.menu.ppsz_choices,
1688 msg_Dbg( p_access, "MMI choice: %s", psz_text );
1691 p_sys->pb_slot_mmi_expected[i_slot] = VLC_FALSE;
1692 p_sys->pb_slot_mmi_undisplayed[i_slot] = VLC_TRUE;
1695 /*****************************************************************************
1697 *****************************************************************************/
1698 static void MMIHandle( access_t *p_access, int i_session_id,
1699 uint8_t *p_apdu, int i_size )
1701 int i_tag = APDUGetTag( p_apdu, i_size );
1705 case AOT_DISPLAY_CONTROL:
1708 uint8_t *d = APDUGetLength( p_apdu, &l );
1714 case DCC_SET_MMI_MODE:
1715 if ( l == 2 && d[1] == MM_HIGH_LEVEL )
1716 MMIDisplayReply( p_access, i_session_id );
1718 msg_Err( p_access, "unsupported MMI mode %02x", d[1] );
1722 msg_Err( p_access, "unsupported display control command %02x",
1731 MMIHandleEnq( p_access, i_session_id, p_apdu, i_size );
1736 MMIHandleMenu( p_access, i_session_id, i_tag, p_apdu, i_size );
1740 SessionSendClose( p_access, i_session_id );
1744 msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
1748 /*****************************************************************************
1750 *****************************************************************************/
1751 static void MMIClose( access_t *p_access, int i_session_id )
1753 access_sys_t *p_sys = p_access->p_sys;
1754 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1755 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1757 en50221_MMIFree( &p_mmi->last_object );
1758 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1760 msg_Dbg( p_access, "closing MMI session (%d)", i_session_id );
1761 p_sys->pb_slot_mmi_expected[i_slot] = VLC_FALSE;
1762 p_sys->pb_slot_mmi_undisplayed[i_slot] = VLC_TRUE;
1765 /*****************************************************************************
1767 *****************************************************************************/
1768 static void MMIOpen( access_t *p_access, int i_session_id )
1770 access_sys_t *p_sys = p_access->p_sys;
1773 msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
1775 p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
1776 p_sys->p_sessions[i_session_id - 1].pf_close = MMIClose;
1777 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(mmi_t));
1778 p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1779 p_mmi->last_object.i_object_type = EN50221_MMI_NONE;
1787 /*****************************************************************************
1788 * InitSlot: Open the transport layer
1789 *****************************************************************************/
1790 #define MAX_TC_RETRIES 20
1792 static int InitSlot( access_t * p_access, int i_slot )
1794 access_sys_t *p_sys = p_access->p_sys;
1797 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1800 msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
1802 return VLC_EGENERIC;
1805 /* This is out of the spec */
1806 for ( i = 0; i < MAX_TC_RETRIES; i++ )
1809 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
1810 && i_tag == T_CTC_REPLY )
1812 p_sys->pb_active_slot[i_slot] = VLC_TRUE;
1816 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1820 "en50221_Init: couldn't send TPDU on slot %d",
1826 if ( p_sys->pb_active_slot[i_slot] )
1828 p_sys->i_ca_timeout = 100000;
1832 return VLC_EGENERIC;
1837 * External entry points
1840 /*****************************************************************************
1841 * en50221_Init : Initialize the CAM for en50221
1842 *****************************************************************************/
1843 int E_(en50221_Init)( access_t * p_access )
1845 access_sys_t *p_sys = p_access->p_sys;
1847 if( p_sys->i_ca_type & CA_CI_LINK )
1850 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1852 if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
1854 msg_Err( p_access, "en50221_Init: couldn't reset slot %d",
1859 p_sys->i_ca_timeout = 100000;
1860 /* Wait a bit otherwise it doesn't initialize properly... */
1867 struct ca_slot_info info;
1870 /* We don't reset the CAM in that case because it's done by the
1872 if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &info ) < 0 )
1874 msg_Err( p_access, "en50221_Init: couldn't get slot info" );
1875 close( p_sys->i_ca_handle );
1876 p_sys->i_ca_handle = 0;
1877 return VLC_EGENERIC;
1879 if( info.flags == 0 )
1881 msg_Err( p_access, "en50221_Init: no CAM inserted" );
1882 close( p_sys->i_ca_handle );
1883 p_sys->i_ca_handle = 0;
1884 return VLC_EGENERIC;
1887 /* Allocate a dummy sessions */
1888 p_sys->p_sessions[ 0 ].i_resource_id = RI_CONDITIONAL_ACCESS_SUPPORT;
1890 /* Get application info to find out which cam we are using and make
1891 sure everything is ready to play */
1894 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1895 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1896 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1897 memset( &ca_msg.msg[3], 0, 253 );
1898 APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1899 if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1901 msg_Err( p_access, "en50221_Init: failed getting message" );
1902 return VLC_EGENERIC;
1905 #if HLCI_WAIT_CAM_READY
1906 while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1908 if( p_access->b_die ) return VLC_EGENERIC;
1910 msg_Dbg( p_access, "CAM: please wait" );
1911 APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1913 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1914 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1915 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1916 memset( &ca_msg.msg[3], 0, 253 );
1917 if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1919 msg_Err( p_access, "en50221_Init: failed getting message" );
1920 return VLC_EGENERIC;
1922 msg_Dbg( p_access, "en50221_Init: Got length: %d, tag: 0x%x", ca_msg.length, APDUGetTag( ca_msg.msg, ca_msg.length ) );
1925 if( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1927 msg_Err( p_access, "CAM returns garbage as application info!" );
1928 return VLC_EGENERIC;
1931 msg_Dbg( p_access, "found CAM %s using id 0x%x", &ca_msg.msg[12],
1932 (ca_msg.msg[8]<<8)|ca_msg.msg[9] );
1937 /*****************************************************************************
1938 * en50221_Poll : Poll the CAM for TPDUs
1939 *****************************************************************************/
1940 int E_(en50221_Poll)( access_t * p_access )
1942 access_sys_t *p_sys = p_access->p_sys;
1946 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1949 ca_slot_info_t sinfo;
1952 if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
1954 msg_Err( p_access, "en50221_Poll: couldn't get info on slot %d",
1959 if ( !(sinfo.flags & CA_CI_MODULE_READY) )
1961 if ( p_sys->pb_active_slot[i_slot] )
1963 msg_Dbg( p_access, "en50221_Poll: slot %d has been removed",
1965 p_sys->pb_active_slot[i_slot] = VLC_FALSE;
1966 p_sys->pb_slot_mmi_expected[i_slot] = VLC_FALSE;
1967 p_sys->pb_slot_mmi_undisplayed[i_slot] = VLC_FALSE;
1969 /* Close all sessions for this slot. */
1970 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS;
1973 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1974 && p_sys->p_sessions[i_session_id - 1].i_slot
1977 if ( p_sys->p_sessions[i_session_id - 1].pf_close
1980 p_sys->p_sessions[i_session_id - 1].pf_close(
1981 p_access, i_session_id );
1983 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
1990 else if ( !p_sys->pb_active_slot[i_slot] )
1992 InitSlot( p_access, i_slot );
1994 if ( !p_sys->pb_active_slot[i_slot] )
1996 msg_Dbg( p_access, "en50221_Poll: resetting slot %d", i_slot );
1998 if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
2000 msg_Err( p_access, "en50221_Poll: couldn't reset slot %d",
2006 msg_Dbg( p_access, "en50221_Poll: slot %d is active",
2010 if ( !p_sys->pb_tc_has_data[i_slot] )
2012 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, NULL, 0 ) !=
2016 "en50221_Poll: couldn't send TPDU on slot %d",
2020 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) !=
2024 "en50221_Poll: couldn't recv TPDU on slot %d",
2030 while ( p_sys->pb_tc_has_data[i_slot] )
2032 uint8_t p_tpdu[MAX_TPDU_SIZE];
2033 int i_size, i_session_size;
2036 if ( TPDUSend( p_access, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
2039 "en50221_Poll: couldn't send TPDU on slot %d",
2043 if ( TPDURecv( p_access, i_slot, &i_tag, p_tpdu, &i_size ) !=
2047 "en50221_Poll: couldn't recv TPDU on slot %d",
2052 p_session = GetLength( &p_tpdu[3], &i_session_size );
2053 if ( i_session_size <= 1 )
2059 if ( i_tag != T_DATA_LAST )
2062 "en50221_Poll: fragmented TPDU not supported" );
2066 SPDUHandle( p_access, i_slot, p_session, i_session_size );
2070 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2072 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2073 && p_sys->p_sessions[i_session_id - 1].pf_manage )
2075 p_sys->p_sessions[i_session_id - 1].pf_manage( p_access,
2084 /*****************************************************************************
2085 * en50221_SetCAPMT :
2086 *****************************************************************************/
2087 int E_(en50221_SetCAPMT)( access_t * p_access, dvbpsi_pmt_t *p_pmt )
2089 access_sys_t *p_sys = p_access->p_sys;
2090 int i, i_session_id;
2091 vlc_bool_t b_update = VLC_FALSE;
2092 vlc_bool_t b_needs_descrambling = CAPMTNeedsDescrambling( p_pmt );
2094 for ( i = 0; i < MAX_PROGRAMS; i++ )
2096 if ( p_sys->pp_selected_programs[i] != NULL
2097 && p_sys->pp_selected_programs[i]->i_program_number
2098 == p_pmt->i_program_number )
2100 b_update = VLC_TRUE;
2102 if ( !b_needs_descrambling )
2104 dvbpsi_DeletePMT( p_pmt );
2105 p_pmt = p_sys->pp_selected_programs[i];
2106 p_sys->pp_selected_programs[i] = NULL;
2108 else if( p_pmt != p_sys->pp_selected_programs[i] )
2110 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
2111 p_sys->pp_selected_programs[i] = p_pmt;
2118 if ( !b_update && b_needs_descrambling )
2120 for ( i = 0; i < MAX_PROGRAMS; i++ )
2122 if ( p_sys->pp_selected_programs[i] == NULL )
2124 p_sys->pp_selected_programs[i] = p_pmt;
2130 if ( b_update || b_needs_descrambling )
2132 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2134 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2135 == RI_CONDITIONAL_ACCESS_SUPPORT )
2137 if ( b_update && b_needs_descrambling )
2138 CAPMTUpdate( p_access, i_session_id, p_pmt );
2139 else if ( b_update )
2140 CAPMTDelete( p_access, i_session_id, p_pmt );
2142 CAPMTAdd( p_access, i_session_id, p_pmt );
2147 if ( !b_needs_descrambling )
2149 dvbpsi_DeletePMT( p_pmt );
2155 /*****************************************************************************
2157 *****************************************************************************/
2158 int E_(en50221_OpenMMI)( access_t * p_access, int i_slot )
2160 access_sys_t *p_sys = p_access->p_sys;
2162 if( p_sys->i_ca_type & CA_CI_LINK )
2165 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2167 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2168 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2171 "MMI menu is already opened on slot %d (session=%d)",
2172 i_slot, i_session_id );
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
2180 == RI_APPLICATION_INFORMATION
2181 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2183 ApplicationInformationEnterMenu( p_access, i_session_id );
2188 msg_Err( p_access, "no application information on slot %d", i_slot );
2189 return VLC_EGENERIC;
2193 msg_Err( p_access, "MMI menu not supported" );
2194 return VLC_EGENERIC;
2198 /*****************************************************************************
2199 * en50221_CloseMMI :
2200 *****************************************************************************/
2201 int E_(en50221_CloseMMI)( access_t * p_access, int i_slot )
2203 access_sys_t *p_sys = p_access->p_sys;
2205 if( p_sys->i_ca_type & CA_CI_LINK )
2208 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2210 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2211 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2213 MMISendClose( p_access, i_session_id );
2218 msg_Warn( p_access, "closing a non-existing MMI session on slot %d",
2220 return VLC_EGENERIC;
2224 msg_Err( p_access, "MMI menu not supported" );
2225 return VLC_EGENERIC;
2229 /*****************************************************************************
2230 * en50221_GetMMIObject :
2231 *****************************************************************************/
2232 en50221_mmi_object_t *E_(en50221_GetMMIObject)( access_t * p_access,
2235 access_sys_t *p_sys = p_access->p_sys;
2238 if ( p_sys->pb_slot_mmi_expected[i_slot] == VLC_TRUE )
2239 return NULL; /* should not happen */
2241 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2243 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2244 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2247 (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
2248 if ( p_mmi == NULL )
2249 return NULL; /* should not happen */
2250 return &p_mmi->last_object;
2258 /*****************************************************************************
2259 * en50221_SendMMIObject :
2260 *****************************************************************************/
2261 void E_(en50221_SendMMIObject)( access_t * p_access, int i_slot,
2262 en50221_mmi_object_t *p_object )
2264 access_sys_t *p_sys = p_access->p_sys;
2267 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2269 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2270 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2272 MMISendObject( p_access, i_session_id, p_object );
2277 msg_Err( p_access, "SendMMIObject when no MMI session is opened !" );
2280 /*****************************************************************************
2282 *****************************************************************************/
2283 void E_(en50221_End)( access_t * p_access )
2285 access_sys_t *p_sys = p_access->p_sys;
2286 int i_session_id, i;
2288 for ( i = 0; i < MAX_PROGRAMS; i++ )
2290 if ( p_sys->pp_selected_programs[i] != NULL )
2292 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
2296 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2298 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2299 && p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
2301 p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
2306 /* Leave the CAM configured, so that it can be reused in another
2310 static inline void *FixUTF8( char *p )
2316 static char *dvbsi_to_utf8( char *psz_instring, size_t i_length )
2318 const char *psz_encoding;
2319 char *psz_stringstart, *psz_outstring, *psz_tmp;
2320 char psz_encbuf[12];
2322 vlc_iconv_t iconv_handle;
2323 if( i_length < 1 ) return NULL;
2324 if( psz_instring[0] < 0 || psz_instring[0] >= 0x20 )
2326 psz_stringstart = psz_instring;
2327 psz_encoding = "ISO_8859-1"; /* should be ISO6937 according to spec, but this seems to be the one used */
2328 } else switch( psz_instring[0] )
2331 psz_stringstart = &psz_instring[1];
2332 psz_encoding = "ISO_8859-5";
2335 psz_stringstart = &psz_instring[1];
2336 psz_encoding = "ISO_8859-6";
2339 psz_stringstart = &psz_instring[1];
2340 psz_encoding = "ISO_8859-7";
2343 psz_stringstart = &psz_instring[1];
2344 psz_encoding = "ISO_8859-8";
2347 psz_stringstart = &psz_instring[1];
2348 psz_encoding = "ISO_8859-9";
2351 psz_stringstart = &psz_instring[1];
2352 psz_encoding = "ISO_8859-10";
2355 psz_stringstart = &psz_instring[1];
2356 psz_encoding = "ISO_8859-11";
2359 psz_stringstart = &psz_instring[1]; /*possibly reserved?*/
2360 psz_encoding = "ISO_8859-12";
2363 psz_stringstart = &psz_instring[1];
2364 psz_encoding = "ISO_8859-13";
2367 psz_stringstart = &psz_instring[1];
2368 psz_encoding = "ISO_8859-14";
2371 psz_stringstart = &psz_instring[1];
2372 psz_encoding = "ISO_8859-15";
2375 if( i_length < 3 || psz_instring[1] != '\0' || psz_instring[2] > 0x0f
2376 || psz_instring[2] == 0 )
2377 return FixUTF8(strndup(psz_instring,i_length));
2378 sprintf( psz_encbuf, "ISO_8859-%d", psz_instring[2] );
2379 psz_stringstart = &psz_instring[3];
2380 psz_encoding = psz_encbuf;
2383 psz_stringstart = &psz_instring[1];
2384 psz_encoding = "UTF-16";
2387 psz_stringstart = &psz_instring[1];
2388 psz_encoding = "KSC5601-1987";
2391 psz_stringstart = &psz_instring[1];
2392 psz_encoding = "GB2312";/*GB-2312-1980 */
2395 psz_stringstart = &psz_instring[1];
2396 psz_encoding = "BIG-5";
2399 return FixUTF8(strndup(&psz_instring[1],i_length-1));
2403 return FixUTF8(strndup(psz_instring,i_length));
2405 iconv_handle = vlc_iconv_open( "UTF-8", psz_encoding );
2406 i_in = i_length - (psz_stringstart - psz_instring );
2408 psz_outstring = psz_tmp = (char*)malloc( i_out * sizeof(char) + 1 );
2409 vlc_iconv( iconv_handle, &psz_stringstart, &i_in, &psz_tmp, &i_out );
2410 vlc_iconv_close( iconv_handle );
2412 return psz_outstring;