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/input.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"
69 #define HLCI_WAIT_CAM_READY 0
70 #define CAM_PROG_MAX MAX_PROGRAMS
72 static void ResourceManagerOpen( access_t * p_access, int i_session_id );
73 static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
74 static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
75 static void DateTimeOpen( access_t * p_access, int i_session_id );
76 static void MMIOpen( access_t * p_access, int i_session_id );
78 /*****************************************************************************
80 *****************************************************************************/
81 #define SIZE_INDICATOR 0x80
83 static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
85 *pi_length = *p_data++;
87 if ( (*pi_length & SIZE_INDICATOR) != 0 )
89 int l = *pi_length & ~SIZE_INDICATOR;
93 for ( i = 0; i < l; i++ )
94 *pi_length = (*pi_length << 8) | *p_data++;
100 static uint8_t *SetLength( uint8_t *p_data, int i_length )
104 if ( i_length < 128 )
108 else if ( i_length < 256 )
110 *p++ = SIZE_INDICATOR | 0x1;
113 else if ( i_length < 65536 )
115 *p++ = SIZE_INDICATOR | 0x2;
116 *p++ = i_length >> 8;
117 *p++ = i_length & 0xff;
119 else if ( i_length < 16777216 )
121 *p++ = SIZE_INDICATOR | 0x3;
122 *p++ = i_length >> 16;
123 *p++ = (i_length >> 8) & 0xff;
124 *p++ = i_length & 0xff;
128 *p++ = SIZE_INDICATOR | 0x4;
129 *p++ = i_length >> 24;
130 *p++ = (i_length >> 16) & 0xff;
131 *p++ = (i_length >> 8) & 0xff;
132 *p++ = i_length & 0xff;
143 #define MAX_TPDU_SIZE 2048
144 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
146 #define DATA_INDICATOR 0x80
150 #define T_CREATE_TC 0x82
151 #define T_CTC_REPLY 0x83
152 #define T_DELETE_TC 0x84
153 #define T_DTC_REPLY 0x85
154 #define T_REQUEST_TC 0x86
155 #define T_NEW_TC 0x87
156 #define T_TC_ERROR 0x88
157 #define T_DATA_LAST 0xA0
158 #define T_DATA_MORE 0xA1
160 static void Dump( vlc_bool_t b_outgoing, uint8_t *p_data, int i_size )
165 fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
166 for ( i = 0; i < i_size && i < MAX_DUMP; i++)
167 fprintf(stderr, "%02X ", p_data[i]);
168 fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
172 /*****************************************************************************
174 *****************************************************************************/
175 static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
176 const uint8_t *p_content, int i_length )
178 access_sys_t *p_sys = p_access->p_sys;
179 uint8_t i_tcid = i_slot + 1;
180 uint8_t p_data[MAX_TPDU_SIZE];
196 p_data[3] = 1; /* length */
203 p_data[3] = 2; /* length */
205 p_data[5] = p_content[0];
212 /* i_length <= MAX_TPDU_DATA */
213 uint8_t *p = p_data + 3;
214 p = SetLength( p, i_length + 1 );
218 memcpy( p, p_content, i_length );
219 i_size = i_length + (p - p_data);
226 Dump( VLC_TRUE, p_data, i_size );
228 if ( write( p_sys->i_ca_handle, p_data, i_size ) != i_size )
230 msg_Err( p_access, "cannot write to CAM device (%s)",
239 /*****************************************************************************
241 *****************************************************************************/
242 #define CAM_READ_TIMEOUT 3500 // ms
244 static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
245 uint8_t *p_data, int *pi_size )
247 access_sys_t *p_sys = p_access->p_sys;
248 uint8_t i_tcid = i_slot + 1;
250 struct pollfd pfd[1];
252 pfd[0].fd = p_sys->i_ca_handle;
253 pfd[0].events = POLLIN;
254 if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
256 msg_Err( p_access, "cannot poll from CAM device" );
260 if ( pi_size == NULL )
262 p_data = malloc( MAX_TPDU_SIZE );
267 i_size = read( p_sys->i_ca_handle, p_data, MAX_TPDU_SIZE );
269 if ( i_size >= 0 || errno != EINTR )
275 msg_Err( p_access, "cannot read from CAM device (%d:%s)", i_size,
280 if ( p_data[1] != i_tcid )
282 msg_Err( p_access, "invalid read from CAM device (%d instead of %d)",
288 p_sys->pb_tc_has_data[i_slot] = (i_size >= 4
289 && p_data[i_size - 4] == T_SB
290 && p_data[i_size - 3] == 2
291 && (p_data[i_size - 1] & DATA_INDICATOR))
292 ? VLC_TRUE : VLC_FALSE;
294 Dump( VLC_FALSE, p_data, i_size );
296 if ( pi_size == NULL )
309 #define ST_SESSION_NUMBER 0x90
310 #define ST_OPEN_SESSION_REQUEST 0x91
311 #define ST_OPEN_SESSION_RESPONSE 0x92
312 #define ST_CREATE_SESSION 0x93
313 #define ST_CREATE_SESSION_RESPONSE 0x94
314 #define ST_CLOSE_SESSION_REQUEST 0x95
315 #define ST_CLOSE_SESSION_RESPONSE 0x96
318 #define SS_NOT_ALLOCATED 0xF0
320 #define RI_RESOURCE_MANAGER 0x00010041
321 #define RI_APPLICATION_INFORMATION 0x00020041
322 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
323 #define RI_HOST_CONTROL 0x00200041
324 #define RI_DATE_TIME 0x00240041
325 #define RI_MMI 0x00400041
327 static int ResourceIdToInt( uint8_t *p_data )
329 return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
330 | ((int)p_data[2] << 8) | p_data[3];
333 /*****************************************************************************
335 *****************************************************************************/
336 static int SPDUSend( access_t * p_access, int i_session_id,
337 uint8_t *p_data, int i_size )
339 access_sys_t *p_sys = p_access->p_sys;
340 uint8_t *p_spdu = malloc( i_size + 4 );
343 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
345 *p++ = ST_SESSION_NUMBER;
347 *p++ = (i_session_id >> 8);
348 *p++ = i_session_id & 0xff;
350 memcpy( p, p_data, i_size );
357 if ( i_size > MAX_TPDU_DATA )
359 if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
360 MAX_TPDU_DATA ) != VLC_SUCCESS )
362 msg_Err( p_access, "couldn't send TPDU on session %d",
368 i_size -= MAX_TPDU_DATA;
372 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
375 msg_Err( p_access, "couldn't send TPDU on session %d",
383 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
386 msg_Err( p_access, "couldn't recv TPDU on session %d",
397 /*****************************************************************************
399 *****************************************************************************/
400 static void SessionOpen( access_t * p_access, uint8_t i_slot,
401 uint8_t *p_spdu, int i_size )
403 access_sys_t *p_sys = p_access->p_sys;
405 int i_resource_id = ResourceIdToInt( &p_spdu[2] );
406 uint8_t p_response[16];
407 int i_status = SS_NOT_ALLOCATED;
410 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
412 if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
415 if ( i_session_id == MAX_SESSIONS )
417 msg_Err( p_access, "too many sessions !" );
420 p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
421 p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
422 p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
423 p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
425 if ( i_resource_id == RI_RESOURCE_MANAGER
426 || i_resource_id == RI_APPLICATION_INFORMATION
427 || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
428 || i_resource_id == RI_DATE_TIME
429 || i_resource_id == RI_MMI )
434 p_response[0] = ST_OPEN_SESSION_RESPONSE;
436 p_response[2] = i_status;
437 p_response[3] = p_spdu[2];
438 p_response[4] = p_spdu[3];
439 p_response[5] = p_spdu[4];
440 p_response[6] = p_spdu[5];
441 p_response[7] = i_session_id >> 8;
442 p_response[8] = i_session_id & 0xff;
444 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) !=
448 "SessionOpen: couldn't send TPDU on slot %d", i_slot );
451 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
454 "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
458 switch ( i_resource_id )
460 case RI_RESOURCE_MANAGER:
461 ResourceManagerOpen( p_access, i_session_id ); break;
462 case RI_APPLICATION_INFORMATION:
463 ApplicationInformationOpen( p_access, i_session_id ); break;
464 case RI_CONDITIONAL_ACCESS_SUPPORT:
465 ConditionalAccessOpen( p_access, i_session_id ); break;
467 DateTimeOpen( p_access, i_session_id ); break;
469 MMIOpen( p_access, i_session_id ); break;
471 case RI_HOST_CONTROL:
473 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
474 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
479 /* unused code for the moment - commented out to keep gcc happy */
480 /*****************************************************************************
482 *****************************************************************************/
483 static void SessionCreate( access_t * p_access, int i_slot, int i_resource_id )
485 access_sys_t *p_sys = p_access->p_sys;
486 uint8_t p_response[16];
490 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
492 if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
495 if ( i_session_id == MAX_SESSIONS )
497 msg_Err( p_access, "too many sessions !" );
500 p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
501 p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
502 p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
503 p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
504 p_sys->p_sessions[i_session_id - 1].p_sys = NULL;
506 p_response[0] = ST_CREATE_SESSION;
508 p_response[2] = i_resource_id >> 24;
509 p_response[3] = (i_resource_id >> 16) & 0xff;
510 p_response[4] = (i_resource_id >> 8) & 0xff;
511 p_response[5] = i_resource_id & 0xff;
512 p_response[6] = i_session_id >> 8;
513 p_response[7] = i_session_id & 0xff;
515 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
519 "SessionCreate: couldn't send TPDU on slot %d", i_slot );
522 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
525 "SessionCreate: couldn't recv TPDU on slot %d", i_slot );
531 /*****************************************************************************
532 * SessionCreateResponse
533 *****************************************************************************/
534 static void SessionCreateResponse( access_t * p_access, uint8_t i_slot,
535 uint8_t *p_spdu, int i_size )
537 access_sys_t *p_sys = p_access->p_sys;
538 int i_status = p_spdu[2];
539 int i_resource_id = ResourceIdToInt( &p_spdu[3] );
540 int i_session_id = ((int)p_spdu[7] << 8) | p_spdu[8];
542 if ( i_status != SS_OK )
544 msg_Err( p_access, "SessionCreateResponse: failed to open session %d"
545 " resource=0x%x status=0x%x", i_session_id, i_resource_id,
547 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
551 switch ( i_resource_id )
553 case RI_RESOURCE_MANAGER:
554 ResourceManagerOpen( p_access, i_session_id ); break;
555 case RI_APPLICATION_INFORMATION:
556 ApplicationInformationOpen( p_access, i_session_id ); break;
557 case RI_CONDITIONAL_ACCESS_SUPPORT:
558 ConditionalAccessOpen( p_access, i_session_id ); break;
560 DateTimeOpen( p_access, i_session_id ); break;
562 MMIOpen( p_access, i_session_id ); break;
564 case RI_HOST_CONTROL:
566 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
567 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
571 /*****************************************************************************
573 *****************************************************************************/
574 static void SessionSendClose( access_t * p_access, int i_session_id )
576 access_sys_t *p_sys = p_access->p_sys;
577 uint8_t p_response[16];
579 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
581 p_response[0] = ST_CLOSE_SESSION_REQUEST;
583 p_response[2] = i_session_id >> 8;
584 p_response[3] = i_session_id & 0xff;
586 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
590 "SessionSendClose: couldn't send TPDU on slot %d", i_slot );
593 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
596 "SessionSendClose: couldn't recv TPDU on slot %d", i_slot );
601 /*****************************************************************************
603 *****************************************************************************/
604 static void SessionClose( access_t * p_access, int i_session_id )
606 access_sys_t *p_sys = p_access->p_sys;
607 uint8_t p_response[16];
609 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
611 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
612 p_sys->p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
613 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
615 p_response[0] = ST_CLOSE_SESSION_RESPONSE;
617 p_response[2] = SS_OK;
618 p_response[3] = i_session_id >> 8;
619 p_response[4] = i_session_id & 0xff;
621 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
625 "SessionClose: couldn't send TPDU on slot %d", i_slot );
628 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
631 "SessionClose: couldn't recv TPDU on slot %d", i_slot );
636 /*****************************************************************************
638 *****************************************************************************/
639 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
640 uint8_t *p_spdu, int i_size )
642 access_sys_t *p_sys = p_access->p_sys;
647 case ST_SESSION_NUMBER:
650 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
651 p_sys->p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
652 p_spdu + 4, i_size - 4 );
655 case ST_OPEN_SESSION_REQUEST:
656 if ( i_size != 6 || p_spdu[1] != 0x4 )
658 SessionOpen( p_access, i_slot, p_spdu, i_size );
661 case ST_CREATE_SESSION_RESPONSE:
662 if ( i_size != 9 || p_spdu[1] != 0x7 )
664 SessionCreateResponse( p_access, i_slot, p_spdu, i_size );
667 case ST_CLOSE_SESSION_REQUEST:
668 if ( i_size != 4 || p_spdu[1] != 0x2 )
670 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
671 SessionClose( p_access, i_session_id );
674 case ST_CLOSE_SESSION_RESPONSE:
675 if ( i_size != 5 || p_spdu[1] != 0x3 )
677 i_session_id = ((int)p_spdu[3] << 8) | p_spdu[4];
680 msg_Err( p_access, "closing a session which is not allocated (%d)",
685 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
686 p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
688 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
693 msg_Err( p_access, "unexpected tag in SPDUHandle (%x)", p_spdu[0] );
703 #define AOT_NONE 0x000000
704 #define AOT_PROFILE_ENQ 0x9F8010
705 #define AOT_PROFILE 0x9F8011
706 #define AOT_PROFILE_CHANGE 0x9F8012
707 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
708 #define AOT_APPLICATION_INFO 0x9F8021
709 #define AOT_ENTER_MENU 0x9F8022
710 #define AOT_CA_INFO_ENQ 0x9F8030
711 #define AOT_CA_INFO 0x9F8031
712 #define AOT_CA_PMT 0x9F8032
713 #define AOT_CA_PMT_REPLY 0x9F8033
714 #define AOT_TUNE 0x9F8400
715 #define AOT_REPLACE 0x9F8401
716 #define AOT_CLEAR_REPLACE 0x9F8402
717 #define AOT_ASK_RELEASE 0x9F8403
718 #define AOT_DATE_TIME_ENQ 0x9F8440
719 #define AOT_DATE_TIME 0x9F8441
720 #define AOT_CLOSE_MMI 0x9F8800
721 #define AOT_DISPLAY_CONTROL 0x9F8801
722 #define AOT_DISPLAY_REPLY 0x9F8802
723 #define AOT_TEXT_LAST 0x9F8803
724 #define AOT_TEXT_MORE 0x9F8804
725 #define AOT_KEYPAD_CONTROL 0x9F8805
726 #define AOT_KEYPRESS 0x9F8806
727 #define AOT_ENQ 0x9F8807
728 #define AOT_ANSW 0x9F8808
729 #define AOT_MENU_LAST 0x9F8809
730 #define AOT_MENU_MORE 0x9F880A
731 #define AOT_MENU_ANSW 0x9F880B
732 #define AOT_LIST_LAST 0x9F880C
733 #define AOT_LIST_MORE 0x9F880D
734 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
735 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
736 #define AOT_DISPLAY_MESSAGE 0x9F8810
737 #define AOT_SCENE_END_MARK 0x9F8811
738 #define AOT_SCENE_DONE 0x9F8812
739 #define AOT_SCENE_CONTROL 0x9F8813
740 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
741 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
742 #define AOT_FLUSH_DOWNLOAD 0x9F8816
743 #define AOT_DOWNLOAD_REPLY 0x9F8817
744 #define AOT_COMMS_CMD 0x9F8C00
745 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
746 #define AOT_COMMS_REPLY 0x9F8C02
747 #define AOT_COMMS_SEND_LAST 0x9F8C03
748 #define AOT_COMMS_SEND_MORE 0x9F8C04
749 #define AOT_COMMS_RCV_LAST 0x9F8C05
750 #define AOT_COMMS_RCV_MORE 0x9F8C06
752 /*****************************************************************************
754 *****************************************************************************/
755 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
760 for ( i = 0; i < 3; i++ )
761 t = (t << 8) | *p_apdu++;
768 /*****************************************************************************
770 *****************************************************************************/
771 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
773 return GetLength( &p_apdu[3], pi_size );
776 /*****************************************************************************
778 *****************************************************************************/
779 static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
780 uint8_t *p_data, int i_size )
782 access_sys_t *p_sys = p_access->p_sys;
783 uint8_t *p_apdu = malloc( i_size + 12 );
788 *p++ = (i_tag >> 16);
789 *p++ = (i_tag >> 8) & 0xff;
791 p = SetLength( p, i_size );
793 memcpy( p, p_data, i_size );
794 if ( p_sys->i_ca_type == CA_CI_LINK )
796 i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
800 if ( i_size + p - p_apdu > 256 )
802 msg_Err( p_access, "CAM: apdu overflow" );
803 i_ret = VLC_EGENERIC;
808 ca_msg.length = i_size + p - p_apdu;
809 if ( i_size == 0 ) ca_msg.length=3;
810 psz_hex = (char*)malloc( ca_msg.length*3 + 1);
811 memcpy( ca_msg.msg, p_apdu, i_size + p - p_apdu );
812 i_ret = ioctl(p_sys->i_ca_handle, CA_SEND_MSG, &ca_msg );
815 msg_Err( p_access, "Error sending to CAM: %s", strerror(errno) );
816 i_ret = VLC_EGENERIC;
828 /*****************************************************************************
829 * ResourceManagerHandle
830 *****************************************************************************/
831 static void ResourceManagerHandle( access_t * p_access, int i_session_id,
832 uint8_t *p_apdu, int i_size )
834 int i_tag = APDUGetTag( p_apdu, i_size );
838 case AOT_PROFILE_ENQ:
840 int resources[] = { htonl(RI_RESOURCE_MANAGER),
841 htonl(RI_APPLICATION_INFORMATION),
842 htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
846 APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
851 APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
855 msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
860 /*****************************************************************************
861 * ResourceManagerOpen
862 *****************************************************************************/
863 static void ResourceManagerOpen( access_t * p_access, int i_session_id )
865 access_sys_t *p_sys = p_access->p_sys;
867 msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
869 p_sys->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
871 APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
875 * Application Information
878 /*****************************************************************************
879 * ApplicationInformationEnterMenu
880 *****************************************************************************/
881 static void ApplicationInformationEnterMenu( access_t * p_access,
884 access_sys_t *p_sys = p_access->p_sys;
885 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
887 msg_Dbg( p_access, "entering MMI menus on session %d", i_session_id );
888 APDUSend( p_access, i_session_id, AOT_ENTER_MENU, NULL, 0 );
889 p_sys->pb_slot_mmi_expected[i_slot] = VLC_TRUE;
892 /*****************************************************************************
893 * ApplicationInformationHandle
894 *****************************************************************************/
895 static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
896 uint8_t *p_apdu, int i_size )
898 int i_tag = APDUGetTag( p_apdu, i_size );
902 case AOT_APPLICATION_INFO:
904 int i_type, i_manufacturer, i_code;
906 uint8_t *d = APDUGetLength( p_apdu, &l );
909 p_apdu[l + 4] = '\0';
912 i_manufacturer = ((int)d[0] << 8) | d[1];
914 i_code = ((int)d[0] << 8) | d[1];
916 d = GetLength( d, &l );
918 msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
919 d, i_type, i_manufacturer, i_code );
924 "unexpected tag in ApplicationInformationHandle (0x%x)",
929 /*****************************************************************************
930 * ApplicationInformationOpen
931 *****************************************************************************/
932 static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
934 access_sys_t *p_sys = p_access->p_sys;
936 msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
938 p_sys->p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
940 APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
947 #define MAX_CASYSTEM_IDS 16
951 uint16_t pi_system_ids[MAX_CASYSTEM_IDS + 1];
954 static vlc_bool_t CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
957 if( !p_ids ) return VLC_TRUE;
959 while ( p_ids->pi_system_ids[i] )
961 if ( p_ids->pi_system_ids[i] == i_id )
969 /*****************************************************************************
970 * CAPMTNeedsDescrambling
971 *****************************************************************************/
972 static vlc_bool_t CAPMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
974 dvbpsi_descriptor_t *p_dr;
975 dvbpsi_pmt_es_t *p_es;
977 for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
979 if( p_dr->i_tag == 0x9 )
985 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
987 for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
988 p_dr = p_dr->p_next )
990 if( p_dr->i_tag == 0x9 )
1000 /*****************************************************************************
1002 *****************************************************************************/
1003 static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr )
1007 while ( p_dr != NULL )
1009 if( p_dr->i_tag == 0x9 )
1011 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1013 if ( CheckSystemID( p_ids, i_sysid ) )
1014 i_cad_size += p_dr->i_length + 2;
1016 p_dr = p_dr->p_next;
1022 static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt,
1023 uint16_t i_program_number, uint8_t i_version,
1024 int i_size, dvbpsi_descriptor_t *p_dr,
1030 p_data = malloc( 7 + i_size );
1032 p_data = malloc( 6 );
1034 p_data[0] = i_list_mgt;
1035 p_data[1] = i_program_number >> 8;
1036 p_data[2] = i_program_number & 0xff;
1037 p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
1043 p_data[4] = (i_size + 1) >> 8;
1044 p_data[5] = (i_size + 1) & 0xff;
1048 while ( p_dr != NULL )
1050 if( p_dr->i_tag == 0x9 )
1052 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1054 if ( CheckSystemID( p_ids, i_sysid ) )
1057 p_data[i + 1] = p_dr->i_length;
1058 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1059 // p_data[i+4] &= 0x1f;
1060 i += p_dr->i_length + 2;
1063 p_dr = p_dr->p_next;
1075 static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt,
1076 int i_capmt_size, uint8_t i_type, uint16_t i_pid,
1077 int i_size, dvbpsi_descriptor_t *p_dr,
1084 p_data = realloc( p_capmt, i_capmt_size + 6 + i_size );
1086 p_data = realloc( p_capmt, i_capmt_size + 5 );
1091 p_data[i + 1] = i_pid >> 8;
1092 p_data[i + 2] = i_pid & 0xff;
1096 p_data[i + 3] = (i_size + 1) >> 8;
1097 p_data[i + 4] = (i_size + 1) & 0xff;
1098 p_data[i + 5] = i_cmd;
1101 while ( p_dr != NULL )
1103 if( p_dr->i_tag == 0x9 )
1105 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1107 if ( CheckSystemID( p_ids, i_sysid ) )
1110 p_data[i + 1] = p_dr->i_length;
1111 memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1112 i += p_dr->i_length + 2;
1115 p_dr = p_dr->p_next;
1127 static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id,
1128 dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt,
1129 uint8_t i_cmd, int *pi_capmt_size )
1131 access_sys_t *p_sys = p_access->p_sys;
1132 system_ids_t *p_ids =
1133 (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1134 dvbpsi_pmt_es_t *p_es;
1135 int i_cad_size, i_cad_program_size;
1138 i_cad_size = i_cad_program_size =
1139 GetCADSize( p_ids, p_pmt->p_first_descriptor );
1140 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1142 i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor );
1148 "no compatible scrambling system for SID %d on session %d",
1149 p_pmt->i_program_number, i_session_id );
1154 p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number,
1155 p_pmt->i_version, i_cad_program_size,
1156 p_pmt->p_first_descriptor, i_cmd );
1158 if ( i_cad_program_size )
1159 *pi_capmt_size = 7 + i_cad_program_size;
1163 for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1165 i_cad_size = GetCADSize( p_ids, p_es->p_first_descriptor );
1167 if ( i_cad_size || i_cad_program_size )
1169 p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type,
1170 p_es->i_pid, i_cad_size,
1171 p_es->p_first_descriptor, i_cmd );
1173 *pi_capmt_size += 6 + i_cad_size;
1175 *pi_capmt_size += 5;
1182 /*****************************************************************************
1184 *****************************************************************************/
1185 static void CAPMTFirst( access_t * p_access, int i_session_id,
1186 dvbpsi_pmt_t *p_pmt )
1191 msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d",
1192 p_pmt->i_program_number, i_session_id );
1194 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1195 0x3 /* only */, 0x1 /* ok_descrambling */,
1199 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1202 /*****************************************************************************
1204 *****************************************************************************/
1205 static void CAPMTAdd( access_t * p_access, int i_session_id,
1206 dvbpsi_pmt_t *p_pmt )
1211 if( p_access->p_sys->i_selected_programs >= CAM_PROG_MAX )
1213 msg_Warn( p_access, "Not adding CAPMT for SID %d, too many programs",
1214 p_pmt->i_program_number );
1217 p_access->p_sys->i_selected_programs++;
1218 if( p_access->p_sys->i_selected_programs == 1 )
1220 CAPMTFirst( p_access, i_session_id, p_pmt );
1225 msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
1226 p_pmt->i_program_number, i_session_id );
1228 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1229 0x4 /* add */, 0x1 /* ok_descrambling */,
1233 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1236 /*****************************************************************************
1238 *****************************************************************************/
1239 static void CAPMTUpdate( access_t * p_access, int i_session_id,
1240 dvbpsi_pmt_t *p_pmt )
1245 msg_Dbg( p_access, "updating CAPMT for SID %d on session %d",
1246 p_pmt->i_program_number, i_session_id );
1248 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1249 0x5 /* update */, 0x1 /* ok_descrambling */,
1253 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1256 /*****************************************************************************
1258 *****************************************************************************/
1259 static void CAPMTDelete( access_t * p_access, int i_session_id,
1260 dvbpsi_pmt_t *p_pmt )
1265 p_access->p_sys->i_selected_programs--;
1266 msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d",
1267 p_pmt->i_program_number, i_session_id );
1269 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1270 0x5 /* update */, 0x4 /* not selected */,
1274 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1277 /*****************************************************************************
1278 * ConditionalAccessHandle
1279 *****************************************************************************/
1280 static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
1281 uint8_t *p_apdu, int i_size )
1283 access_sys_t *p_sys = p_access->p_sys;
1284 system_ids_t *p_ids =
1285 (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1286 int i_tag = APDUGetTag( p_apdu, i_size );
1294 uint8_t *d = APDUGetLength( p_apdu, &l );
1295 msg_Dbg( p_access, "CA system IDs supported by the application :" );
1297 for ( i = 0; i < l / 2; i++ )
1299 p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
1301 msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] );
1303 p_ids->pi_system_ids[i] = 0;
1305 for ( i = 0; i < MAX_PROGRAMS; i++ )
1307 if ( p_sys->pp_selected_programs[i] != NULL )
1309 CAPMTAdd( p_access, i_session_id,
1310 p_sys->pp_selected_programs[i] );
1318 "unexpected tag in ConditionalAccessHandle (0x%x)",
1323 /*****************************************************************************
1324 * ConditionalAccessClose
1325 *****************************************************************************/
1326 static void ConditionalAccessClose( access_t * p_access, int i_session_id )
1328 access_sys_t *p_sys = p_access->p_sys;
1330 msg_Dbg( p_access, "closing ConditionalAccess session (%d)", i_session_id );
1332 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1335 /*****************************************************************************
1336 * ConditionalAccessOpen
1337 *****************************************************************************/
1338 static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
1340 access_sys_t *p_sys = p_access->p_sys;
1342 msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
1344 p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
1345 p_sys->p_sessions[i_session_id - 1].pf_close = ConditionalAccessClose;
1346 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(system_ids_t));
1347 memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0,
1348 sizeof(system_ids_t) );
1350 APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
1363 /*****************************************************************************
1365 *****************************************************************************/
1366 static void DateTimeSend( access_t * p_access, int i_session_id )
1368 access_sys_t *p_sys = p_access->p_sys;
1369 date_time_t *p_date =
1370 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1372 time_t t = time(NULL);
1376 if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
1378 int Y = tm_gmt.tm_year;
1379 int M = tm_gmt.tm_mon + 1;
1380 int D = tm_gmt.tm_mday;
1381 int L = (M == 1 || M == 2) ? 1 : 0;
1382 int MJD = 14956 + D + (int)((Y - L) * 365.25)
1383 + (int)((M + 1 + L * 12) * 30.6001);
1384 uint8_t p_response[7];
1386 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
1388 p_response[0] = htons(MJD) >> 8;
1389 p_response[1] = htons(MJD) & 0xff;
1390 p_response[2] = DEC2BCD(tm_gmt.tm_hour);
1391 p_response[3] = DEC2BCD(tm_gmt.tm_min);
1392 p_response[4] = DEC2BCD(tm_gmt.tm_sec);
1393 p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
1394 p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
1396 APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
1398 p_date->i_last = mdate();
1402 /*****************************************************************************
1404 *****************************************************************************/
1405 static void DateTimeHandle( access_t * p_access, int i_session_id,
1406 uint8_t *p_apdu, int i_size )
1408 access_sys_t *p_sys = p_access->p_sys;
1409 date_time_t *p_date =
1410 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1412 int i_tag = APDUGetTag( p_apdu, i_size );
1416 case AOT_DATE_TIME_ENQ:
1419 const uint8_t *d = APDUGetLength( p_apdu, &l );
1423 p_date->i_interval = *d;
1424 msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
1425 p_date->i_interval );
1428 p_date->i_interval = 0;
1430 DateTimeSend( p_access, i_session_id );
1434 msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
1438 /*****************************************************************************
1440 *****************************************************************************/
1441 static void DateTimeManage( access_t * p_access, int i_session_id )
1443 access_sys_t *p_sys = p_access->p_sys;
1444 date_time_t *p_date =
1445 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1447 if ( p_date->i_interval
1448 && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
1450 DateTimeSend( p_access, i_session_id );
1454 /*****************************************************************************
1456 *****************************************************************************/
1457 static void DateTimeClose( access_t * p_access, int i_session_id )
1459 access_sys_t *p_sys = p_access->p_sys;
1461 msg_Dbg( p_access, "closing DateTime session (%d)", i_session_id );
1463 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1466 /*****************************************************************************
1468 *****************************************************************************/
1469 static void DateTimeOpen( access_t * p_access, int i_session_id )
1471 access_sys_t *p_sys = p_access->p_sys;
1473 msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
1475 p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
1476 p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
1477 p_sys->p_sessions[i_session_id - 1].pf_close = DateTimeClose;
1478 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t));
1479 memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
1481 DateTimeSend( p_access, i_session_id );
1488 /* Display Control Commands */
1490 #define DCC_SET_MMI_MODE 0x01
1491 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
1492 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
1493 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
1494 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
1498 #define MM_HIGH_LEVEL 0x01
1499 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
1500 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
1502 /* Display Reply IDs */
1504 #define DRI_MMI_MODE_ACK 0x01
1505 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
1506 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
1507 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
1508 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
1509 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
1510 #define DRI_UNKNOWN_MMI_MODE 0xF1
1511 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
1515 #define EF_BLIND 0x01
1519 #define AI_CANCEL 0x00
1520 #define AI_ANSWER 0x01
1524 en50221_mmi_object_t last_object;
1527 /*****************************************************************************
1529 *****************************************************************************/
1530 static void MMISendObject( access_t *p_access, int i_session_id,
1531 en50221_mmi_object_t *p_object )
1533 access_sys_t *p_sys = p_access->p_sys;
1534 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1538 switch ( p_object->i_object_type )
1540 case EN50221_MMI_ANSW:
1542 i_size = 1 + strlen( p_object->u.answ.psz_answ );
1543 p_data = malloc( i_size );
1544 p_data[0] = (p_object->u.answ.b_ok == VLC_TRUE) ? 0x1 : 0x0;
1545 strncpy( &p_data[1], p_object->u.answ.psz_answ, i_size - 1 );
1548 case EN50221_MMI_MENU_ANSW:
1549 i_tag = AOT_MENU_ANSW;
1551 p_data = malloc( i_size );
1552 p_data[0] = p_object->u.menu_answ.i_choice;
1556 msg_Err( p_access, "unknown MMI object %d", p_object->i_object_type );
1560 APDUSend( p_access, i_session_id, i_tag, p_data, i_size );
1563 p_sys->pb_slot_mmi_expected[i_slot] = VLC_TRUE;
1566 /*****************************************************************************
1568 *****************************************************************************/
1569 static void MMISendClose( access_t *p_access, int i_session_id )
1571 access_sys_t *p_sys = p_access->p_sys;
1572 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1574 APDUSend( p_access, i_session_id, AOT_CLOSE_MMI, NULL, 0 );
1576 p_sys->pb_slot_mmi_expected[i_slot] = VLC_TRUE;
1579 /*****************************************************************************
1581 *****************************************************************************/
1582 static void MMIDisplayReply( access_t *p_access, int i_session_id )
1584 uint8_t p_response[2];
1586 p_response[0] = DRI_MMI_MODE_ACK;
1587 p_response[1] = MM_HIGH_LEVEL;
1589 APDUSend( p_access, i_session_id, AOT_DISPLAY_REPLY, p_response, 2 );
1591 msg_Dbg( p_access, "sending DisplayReply on session (%d)", i_session_id );
1594 /*****************************************************************************
1596 *****************************************************************************/
1597 static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size )
1599 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 );
1612 psz_text = malloc( l + 1 );
1613 strncpy( psz_text, (char *)d, l );
1621 /*****************************************************************************
1623 *****************************************************************************/
1624 static void MMIHandleEnq( access_t *p_access, int i_session_id,
1625 uint8_t *p_apdu, int i_size )
1627 access_sys_t *p_sys = p_access->p_sys;
1628 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1629 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1631 uint8_t *d = APDUGetLength( p_apdu, &l );
1633 en50221_MMIFree( &p_mmi->last_object );
1634 p_mmi->last_object.i_object_type = EN50221_MMI_ENQ;
1635 p_mmi->last_object.u.enq.b_blind = (*d & 0x1) ? VLC_TRUE : VLC_FALSE;
1636 d += 2; /* skip answer_text_length because it is not mandatory */
1638 p_mmi->last_object.u.enq.psz_text = malloc( l + 1 );
1639 strncpy( p_mmi->last_object.u.enq.psz_text, (char *)d, l );
1640 p_mmi->last_object.u.enq.psz_text[l] = '\0';
1642 msg_Dbg( p_access, "MMI enq: %s%s", p_mmi->last_object.u.enq.psz_text,
1643 p_mmi->last_object.u.enq.b_blind == VLC_TRUE ? " (blind)" : "" );
1644 p_sys->pb_slot_mmi_expected[i_slot] = VLC_FALSE;
1645 p_sys->pb_slot_mmi_undisplayed[i_slot] = VLC_TRUE;
1648 /*****************************************************************************
1650 *****************************************************************************/
1651 static void MMIHandleMenu( access_t *p_access, int i_session_id, int i_tag,
1652 uint8_t *p_apdu, int i_size )
1654 access_sys_t *p_sys = p_access->p_sys;
1655 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1656 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1658 uint8_t *d = APDUGetLength( p_apdu, &l );
1660 en50221_MMIFree( &p_mmi->last_object );
1661 p_mmi->last_object.i_object_type = (i_tag == AOT_MENU_LAST) ?
1662 EN50221_MMI_MENU : EN50221_MMI_LIST;
1663 p_mmi->last_object.u.menu.i_choices = 0;
1664 p_mmi->last_object.u.menu.ppsz_choices = NULL;
1668 l--; d++; /* choice_nb */
1670 #define GET_FIELD( x ) \
1673 p_mmi->last_object.u.menu.psz_##x \
1674 = MMIGetText( p_access, &d, &l ); \
1675 msg_Dbg( p_access, "MMI " STRINGIFY( x ) ": %s", \
1676 p_mmi->last_object.u.menu.psz_##x ); \
1680 GET_FIELD( subtitle );
1681 GET_FIELD( bottom );
1686 char *psz_text = MMIGetText( p_access, &d, &l );
1687 TAB_APPEND( p_mmi->last_object.u.menu.i_choices,
1688 p_mmi->last_object.u.menu.ppsz_choices,
1690 msg_Dbg( p_access, "MMI choice: %s", psz_text );
1693 p_sys->pb_slot_mmi_expected[i_slot] = VLC_FALSE;
1694 p_sys->pb_slot_mmi_undisplayed[i_slot] = VLC_TRUE;
1697 /*****************************************************************************
1699 *****************************************************************************/
1700 static void MMIHandle( access_t *p_access, int i_session_id,
1701 uint8_t *p_apdu, int i_size )
1703 int i_tag = APDUGetTag( p_apdu, i_size );
1707 case AOT_DISPLAY_CONTROL:
1710 uint8_t *d = APDUGetLength( p_apdu, &l );
1716 case DCC_SET_MMI_MODE:
1717 if ( l == 2 && d[1] == MM_HIGH_LEVEL )
1718 MMIDisplayReply( p_access, i_session_id );
1720 msg_Err( p_access, "unsupported MMI mode %02x", d[1] );
1724 msg_Err( p_access, "unsupported display control command %02x",
1733 MMIHandleEnq( p_access, i_session_id, p_apdu, i_size );
1738 MMIHandleMenu( p_access, i_session_id, i_tag, p_apdu, i_size );
1742 SessionSendClose( p_access, i_session_id );
1746 msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
1750 /*****************************************************************************
1752 *****************************************************************************/
1753 static void MMIClose( access_t *p_access, int i_session_id )
1755 access_sys_t *p_sys = p_access->p_sys;
1756 int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1757 mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1759 en50221_MMIFree( &p_mmi->last_object );
1760 free( p_sys->p_sessions[i_session_id - 1].p_sys );
1762 msg_Dbg( p_access, "closing MMI session (%d)", i_session_id );
1763 p_sys->pb_slot_mmi_expected[i_slot] = VLC_FALSE;
1764 p_sys->pb_slot_mmi_undisplayed[i_slot] = VLC_TRUE;
1767 /*****************************************************************************
1769 *****************************************************************************/
1770 static void MMIOpen( access_t *p_access, int i_session_id )
1772 access_sys_t *p_sys = p_access->p_sys;
1775 msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
1777 p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
1778 p_sys->p_sessions[i_session_id - 1].pf_close = MMIClose;
1779 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(mmi_t));
1780 p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1781 p_mmi->last_object.i_object_type = EN50221_MMI_NONE;
1789 /*****************************************************************************
1790 * InitSlot: Open the transport layer
1791 *****************************************************************************/
1792 #define MAX_TC_RETRIES 20
1794 static int InitSlot( access_t * p_access, int i_slot )
1796 access_sys_t *p_sys = p_access->p_sys;
1799 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1802 msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
1804 return VLC_EGENERIC;
1807 /* This is out of the spec */
1808 for ( i = 0; i < MAX_TC_RETRIES; i++ )
1811 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
1812 && i_tag == T_CTC_REPLY )
1814 p_sys->pb_active_slot[i_slot] = VLC_TRUE;
1818 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1822 "en50221_Init: couldn't send TPDU on slot %d",
1828 if ( p_sys->pb_active_slot[i_slot] )
1830 p_sys->i_ca_timeout = 100000;
1834 return VLC_EGENERIC;
1839 * External entry points
1842 /*****************************************************************************
1843 * en50221_Init : Initialize the CAM for en50221
1844 *****************************************************************************/
1845 int E_(en50221_Init)( access_t * p_access )
1847 access_sys_t *p_sys = p_access->p_sys;
1849 if( p_sys->i_ca_type & CA_CI_LINK )
1852 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1854 if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
1856 msg_Err( p_access, "en50221_Init: couldn't reset slot %d",
1861 p_sys->i_ca_timeout = 100000;
1862 /* Wait a bit otherwise it doesn't initialize properly... */
1869 struct ca_slot_info info;
1871 /* We don't reset the CAM in that case because it's done by the
1873 if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &info ) < 0 )
1875 msg_Err( p_access, "en50221_Init: couldn't get slot info" );
1876 close( p_sys->i_ca_handle );
1877 p_sys->i_ca_handle = 0;
1878 return VLC_EGENERIC;
1880 if( info.flags == 0 )
1882 msg_Err( p_access, "en50221_Init: no CAM inserted" );
1883 close( p_sys->i_ca_handle );
1884 p_sys->i_ca_handle = 0;
1885 return VLC_EGENERIC;
1888 /* Allocate a dummy sessions */
1889 p_sys->p_sessions[ 0 ].i_resource_id = RI_CONDITIONAL_ACCESS_SUPPORT;
1891 /* Get application info to find out which cam we are using and make
1892 sure everything is ready to play */
1895 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1896 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1897 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1898 memset( &ca_msg.msg[3], 0, 253 );
1899 APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1900 if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1902 msg_Err( p_access, "en50221_Init: failed getting message" );
1903 return VLC_EGENERIC;
1906 #if HLCI_WAIT_CAM_READY
1907 while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1909 if( p_access->b_die ) return VLC_EGENERIC;
1911 msg_Dbg( p_access, "CAM: please wait" );
1912 APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1914 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1915 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1916 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1917 memset( &ca_msg.msg[3], 0, 253 );
1918 if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1920 msg_Err( p_access, "en50221_Init: failed getting message" );
1921 return VLC_EGENERIC;
1923 msg_Dbg( p_access, "en50221_Init: Got length: %d, tag: 0x%x", ca_msg.length, APDUGetTag( ca_msg.msg, ca_msg.length ) );
1926 if( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1928 msg_Err( p_access, "CAM returns garbage as application info!" );
1929 return VLC_EGENERIC;
1932 msg_Dbg( p_access, "found CAM %s using id 0x%x", &ca_msg.msg[12],
1933 (ca_msg.msg[8]<<8)|ca_msg.msg[9] );
1938 /*****************************************************************************
1939 * en50221_Poll : Poll the CAM for TPDUs
1940 *****************************************************************************/
1941 int E_(en50221_Poll)( access_t * p_access )
1943 access_sys_t *p_sys = p_access->p_sys;
1947 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1950 ca_slot_info_t sinfo;
1953 if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
1955 msg_Err( p_access, "en50221_Poll: couldn't get info on slot %d",
1960 if ( !(sinfo.flags & CA_CI_MODULE_READY) )
1962 if ( p_sys->pb_active_slot[i_slot] )
1964 msg_Dbg( p_access, "en50221_Poll: slot %d has been removed",
1966 p_sys->pb_active_slot[i_slot] = VLC_FALSE;
1967 p_sys->pb_slot_mmi_expected[i_slot] = VLC_FALSE;
1968 p_sys->pb_slot_mmi_undisplayed[i_slot] = VLC_FALSE;
1970 /* Close all sessions for this slot. */
1971 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS;
1974 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1975 && p_sys->p_sessions[i_session_id - 1].i_slot
1978 if ( p_sys->p_sessions[i_session_id - 1].pf_close
1981 p_sys->p_sessions[i_session_id - 1].pf_close(
1982 p_access, i_session_id );
1984 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
1991 else if ( !p_sys->pb_active_slot[i_slot] )
1993 InitSlot( p_access, i_slot );
1995 if ( !p_sys->pb_active_slot[i_slot] )
1997 msg_Dbg( p_access, "en50221_Poll: resetting slot %d", i_slot );
1999 if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
2001 msg_Err( p_access, "en50221_Poll: couldn't reset slot %d",
2007 msg_Dbg( p_access, "en50221_Poll: slot %d is active",
2011 if ( !p_sys->pb_tc_has_data[i_slot] )
2013 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, NULL, 0 ) !=
2017 "en50221_Poll: couldn't send TPDU on slot %d",
2021 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) !=
2025 "en50221_Poll: couldn't recv TPDU on slot %d",
2031 while ( p_sys->pb_tc_has_data[i_slot] )
2033 uint8_t p_tpdu[MAX_TPDU_SIZE];
2034 int i_size, i_session_size;
2037 if ( TPDUSend( p_access, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
2040 "en50221_Poll: couldn't send TPDU on slot %d",
2044 if ( TPDURecv( p_access, i_slot, &i_tag, p_tpdu, &i_size ) !=
2048 "en50221_Poll: couldn't recv TPDU on slot %d",
2053 p_session = GetLength( &p_tpdu[3], &i_session_size );
2054 if ( i_session_size <= 1 )
2060 if ( i_tag != T_DATA_LAST )
2063 "en50221_Poll: fragmented TPDU not supported" );
2067 SPDUHandle( p_access, i_slot, p_session, i_session_size );
2071 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2073 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2074 && p_sys->p_sessions[i_session_id - 1].pf_manage )
2076 p_sys->p_sessions[i_session_id - 1].pf_manage( p_access,
2085 /*****************************************************************************
2086 * en50221_SetCAPMT :
2087 *****************************************************************************/
2088 int E_(en50221_SetCAPMT)( access_t * p_access, dvbpsi_pmt_t *p_pmt )
2090 access_sys_t *p_sys = p_access->p_sys;
2091 int i, i_session_id;
2092 vlc_bool_t b_update = VLC_FALSE;
2093 vlc_bool_t b_needs_descrambling = CAPMTNeedsDescrambling( p_pmt );
2095 for ( i = 0; i < MAX_PROGRAMS; i++ )
2097 if ( p_sys->pp_selected_programs[i] != NULL
2098 && p_sys->pp_selected_programs[i]->i_program_number
2099 == p_pmt->i_program_number )
2101 b_update = VLC_TRUE;
2103 if ( !b_needs_descrambling )
2105 dvbpsi_DeletePMT( p_pmt );
2106 p_pmt = p_sys->pp_selected_programs[i];
2107 p_sys->pp_selected_programs[i] = NULL;
2109 else if( p_pmt != p_sys->pp_selected_programs[i] )
2111 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
2112 p_sys->pp_selected_programs[i] = p_pmt;
2119 if ( !b_update && b_needs_descrambling )
2121 for ( i = 0; i < MAX_PROGRAMS; i++ )
2123 if ( p_sys->pp_selected_programs[i] == NULL )
2125 p_sys->pp_selected_programs[i] = p_pmt;
2131 if ( b_update || b_needs_descrambling )
2133 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2135 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2136 == RI_CONDITIONAL_ACCESS_SUPPORT )
2138 if ( b_update && b_needs_descrambling )
2139 CAPMTUpdate( p_access, i_session_id, p_pmt );
2140 else if ( b_update )
2141 CAPMTDelete( p_access, i_session_id, p_pmt );
2143 CAPMTAdd( p_access, i_session_id, p_pmt );
2148 if ( !b_needs_descrambling )
2150 dvbpsi_DeletePMT( p_pmt );
2156 /*****************************************************************************
2158 *****************************************************************************/
2159 int E_(en50221_OpenMMI)( access_t * p_access, int i_slot )
2161 access_sys_t *p_sys = p_access->p_sys;
2163 if( p_sys->i_ca_type & CA_CI_LINK )
2166 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2168 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2169 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2172 "MMI menu is already opened on slot %d (session=%d)",
2173 i_slot, i_session_id );
2178 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2180 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2181 == RI_APPLICATION_INFORMATION
2182 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2184 ApplicationInformationEnterMenu( p_access, i_session_id );
2189 msg_Err( p_access, "no application information on slot %d", i_slot );
2190 return VLC_EGENERIC;
2194 msg_Err( p_access, "MMI menu not supported" );
2195 return VLC_EGENERIC;
2199 /*****************************************************************************
2200 * en50221_CloseMMI :
2201 *****************************************************************************/
2202 int E_(en50221_CloseMMI)( access_t * p_access, int i_slot )
2204 access_sys_t *p_sys = p_access->p_sys;
2206 if( p_sys->i_ca_type & CA_CI_LINK )
2209 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2211 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2212 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2214 MMISendClose( p_access, i_session_id );
2219 msg_Warn( p_access, "closing a non-existing MMI session on slot %d",
2221 return VLC_EGENERIC;
2225 msg_Err( p_access, "MMI menu not supported" );
2226 return VLC_EGENERIC;
2230 /*****************************************************************************
2231 * en50221_GetMMIObject :
2232 *****************************************************************************/
2233 en50221_mmi_object_t *E_(en50221_GetMMIObject)( access_t * p_access,
2236 access_sys_t *p_sys = p_access->p_sys;
2239 if ( p_sys->pb_slot_mmi_expected[i_slot] == VLC_TRUE )
2240 return NULL; /* should not happen */
2242 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2244 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2245 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2248 (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
2249 if ( p_mmi == NULL )
2250 return NULL; /* should not happen */
2251 return &p_mmi->last_object;
2259 /*****************************************************************************
2260 * en50221_SendMMIObject :
2261 *****************************************************************************/
2262 void E_(en50221_SendMMIObject)( access_t * p_access, int i_slot,
2263 en50221_mmi_object_t *p_object )
2265 access_sys_t *p_sys = p_access->p_sys;
2268 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2270 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2271 && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2273 MMISendObject( p_access, i_session_id, p_object );
2278 msg_Err( p_access, "SendMMIObject when no MMI session is opened !" );
2281 /*****************************************************************************
2283 *****************************************************************************/
2284 void E_(en50221_End)( access_t * p_access )
2286 access_sys_t *p_sys = p_access->p_sys;
2287 int i_session_id, i;
2289 for ( i = 0; i < MAX_PROGRAMS; i++ )
2291 if ( p_sys->pp_selected_programs[i] != NULL )
2293 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
2297 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2299 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2300 && p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
2302 p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
2307 /* Leave the CAM configured, so that it can be reused in another