1 /*****************************************************************************
2 * en50221.c : implementation of the transport, session and applications
4 *****************************************************************************
5 * Copyright (C) 2004 VideoLAN
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>
43 static void ResourceManagerOpen( access_t * p_access, int i_session_id );
44 static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
45 static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
46 static void DateTimeOpen( access_t * p_access, int i_session_id );
47 static void MMIOpen( access_t * p_access, int i_session_id );
49 /*****************************************************************************
51 *****************************************************************************/
52 #define SIZE_INDICATOR 0x80
54 static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
56 *pi_length = *p_data++;
58 if ( (*pi_length & SIZE_INDICATOR) != 0 )
60 int l = *pi_length & ~SIZE_INDICATOR;
64 for ( i = 0; i < l; i++ )
65 *pi_length = (*pi_length << 8) | *p_data++;
71 static uint8_t *SetLength( uint8_t *p_data, int i_length )
79 else if ( i_length < 256 )
81 *p++ = SIZE_INDICATOR | 0x1;
84 else if ( i_length < 65536 )
86 *p++ = SIZE_INDICATOR | 0x2;
88 *p++ = i_length & 0xff;
90 else if ( i_length < 16777216 )
92 *p++ = SIZE_INDICATOR | 0x3;
93 *p++ = i_length >> 16;
94 *p++ = (i_length >> 8) & 0xff;
95 *p++ = i_length & 0xff;
99 *p++ = SIZE_INDICATOR | 0x4;
100 *p++ = i_length >> 24;
101 *p++ = (i_length >> 16) & 0xff;
102 *p++ = (i_length >> 8) & 0xff;
103 *p++ = i_length & 0xff;
114 #define MAX_TPDU_SIZE 2048
115 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
117 #define DATA_INDICATOR 0x80
121 #define T_CREATE_TC 0x82
122 #define T_CTC_REPLY 0x83
123 #define T_DELETE_TC 0x84
124 #define T_DTC_REPLY 0x85
125 #define T_REQUEST_TC 0x86
126 #define T_NEW_TC 0x87
127 #define T_TC_ERROR 0x88
128 #define T_DATA_LAST 0xA0
129 #define T_DATA_MORE 0xA1
131 static void Dump( vlc_bool_t b_outgoing, uint8_t *p_data, int i_size )
136 fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
137 for ( i = 0; i < i_size && i < MAX_DUMP; i++)
138 fprintf(stderr, "%02X ", p_data[i]);
139 fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
143 /*****************************************************************************
145 *****************************************************************************/
146 static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
147 const uint8_t *p_content, int i_length )
149 access_sys_t *p_sys = p_access->p_sys;
150 uint8_t i_tcid = i_slot + 1;
151 uint8_t p_data[MAX_TPDU_SIZE];
167 p_data[3] = 1; /* length */
174 p_data[3] = 2; /* length */
176 p_data[5] = p_content[0];
183 /* i_length <= MAX_TPDU_DATA */
184 uint8_t *p = p_data + 3;
185 p = SetLength( p, i_length + 1 );
189 memcpy( p, p_content, i_length );
190 i_size = i_length + (p - p_data);
197 Dump( VLC_TRUE, p_data, i_size );
199 if ( write( p_sys->i_ca_handle, p_data, i_size ) != i_size )
201 msg_Err( p_access, "cannot write to CAM device (%s)",
210 /*****************************************************************************
212 *****************************************************************************/
213 #define CAM_READ_TIMEOUT 3500 // ms
215 static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
216 uint8_t *p_data, int *pi_size )
218 access_sys_t *p_sys = p_access->p_sys;
219 uint8_t i_tcid = i_slot + 1;
221 struct pollfd pfd[1];
223 pfd[0].fd = p_sys->i_ca_handle;
224 pfd[0].events = POLLIN;
225 if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
227 msg_Err( p_access, "cannot poll from CAM device" );
231 if ( pi_size == NULL )
233 p_data = malloc( MAX_TPDU_SIZE );
238 i_size = read( p_sys->i_ca_handle, p_data, MAX_TPDU_SIZE );
240 if ( i_size >= 0 || errno != EINTR )
246 msg_Err( p_access, "cannot read from CAM device (%d:%s)", i_size,
251 if ( p_data[1] != i_tcid )
253 msg_Err( p_access, "invalid read from CAM device (%d instead of %d)",
259 p_sys->pb_tc_has_data[i_slot] = (i_size >= 4
260 && p_data[i_size - 4] == T_SB
261 && p_data[i_size - 3] == 2
262 && (p_data[i_size - 1] & DATA_INDICATOR))
263 ? VLC_TRUE : VLC_FALSE;
265 Dump( VLC_FALSE, p_data, i_size );
267 if ( pi_size == NULL )
280 #define ST_SESSION_NUMBER 0x90
281 #define ST_OPEN_SESSION_REQUEST 0x91
282 #define ST_OPEN_SESSION_RESPONSE 0x92
283 #define ST_CREATE_SESSION 0x93
284 #define ST_CREATE_SESSION_RESPONSE 0x94
285 #define ST_CLOSE_SESSION_REQUEST 0x95
286 #define ST_CLOSE_SESSION_RESPONSE 0x96
289 #define SS_NOT_ALLOCATED 0xF0
291 #define RI_RESOURCE_MANAGER 0x00010041
292 #define RI_APPLICATION_INFORMATION 0x00020041
293 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
294 #define RI_HOST_CONTROL 0x00200041
295 #define RI_DATE_TIME 0x00240041
296 #define RI_MMI 0x00400041
298 static int ResourceIdToInt( uint8_t *p_data )
300 return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
301 | ((int)p_data[2] << 8) | p_data[3];
304 /*****************************************************************************
306 *****************************************************************************/
307 static int SPDUSend( access_t * p_access, int i_session_id,
308 uint8_t *p_data, int i_size )
310 access_sys_t *p_sys = p_access->p_sys;
311 uint8_t *p_spdu = malloc( i_size + 4 );
314 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
316 *p++ = ST_SESSION_NUMBER;
318 *p++ = (i_session_id >> 8);
319 *p++ = i_session_id & 0xff;
321 memcpy( p, p_data, i_size );
328 if ( i_size > MAX_TPDU_DATA )
330 if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
331 MAX_TPDU_DATA ) != VLC_SUCCESS )
333 msg_Err( p_access, "couldn't send TPDU on session %d",
339 i_size -= MAX_TPDU_DATA;
343 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
346 msg_Err( p_access, "couldn't send TPDU on session %d",
354 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
357 msg_Err( p_access, "couldn't recv TPDU on session %d",
368 /*****************************************************************************
370 *****************************************************************************/
371 static void SessionOpen( access_t * p_access, uint8_t i_slot,
372 uint8_t *p_spdu, int i_size )
374 access_sys_t *p_sys = p_access->p_sys;
376 int i_resource_id = ResourceIdToInt( &p_spdu[2] );
377 uint8_t p_response[16];
378 int i_status = SS_NOT_ALLOCATED;
381 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
383 if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
386 if ( i_session_id == MAX_SESSIONS )
388 msg_Err( p_access, "too many sessions !" );
391 p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
392 p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
393 p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
394 p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
396 if ( i_resource_id == RI_RESOURCE_MANAGER
397 || i_resource_id == RI_APPLICATION_INFORMATION
398 || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
399 || i_resource_id == RI_DATE_TIME
400 || i_resource_id == RI_MMI )
405 p_response[0] = ST_OPEN_SESSION_RESPONSE;
407 p_response[2] = i_status;
408 p_response[3] = p_spdu[2];
409 p_response[4] = p_spdu[3];
410 p_response[5] = p_spdu[4];
411 p_response[6] = p_spdu[5];
412 p_response[7] = i_session_id >> 8;
413 p_response[8] = i_session_id & 0xff;
415 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) !=
419 "SessionOpen: couldn't send TPDU on slot %d", i_slot );
422 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
425 "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
429 switch ( i_resource_id )
431 case RI_RESOURCE_MANAGER:
432 ResourceManagerOpen( p_access, i_session_id ); break;
433 case RI_APPLICATION_INFORMATION:
434 ApplicationInformationOpen( p_access, i_session_id ); break;
435 case RI_CONDITIONAL_ACCESS_SUPPORT:
436 ConditionalAccessOpen( p_access, i_session_id ); break;
438 DateTimeOpen( p_access, i_session_id ); break;
440 MMIOpen( p_access, i_session_id ); break;
442 case RI_HOST_CONTROL:
444 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
445 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
449 /*****************************************************************************
451 *****************************************************************************/
452 static void SessionClose( access_t * p_access, int i_session_id )
454 access_sys_t *p_sys = p_access->p_sys;
455 uint8_t p_response[16];
457 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
459 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
460 p_sys->p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
461 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
463 p_response[0] = ST_CLOSE_SESSION_RESPONSE;
465 p_response[2] = SS_OK;
466 p_response[3] = i_session_id >> 8;
467 p_response[4] = i_session_id & 0xff;
469 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
473 "SessionOpen: couldn't send TPDU on slot %d", i_slot );
476 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
479 "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
484 /*****************************************************************************
486 *****************************************************************************/
487 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
488 uint8_t *p_spdu, int i_size )
490 access_sys_t *p_sys = p_access->p_sys;
495 case ST_SESSION_NUMBER:
498 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
499 p_sys->p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
500 p_spdu + 4, i_size - 4 );
503 case ST_OPEN_SESSION_REQUEST:
504 if ( i_size != 6 || p_spdu[1] != 0x4 )
506 SessionOpen( p_access, i_slot, p_spdu, i_size );
509 case ST_CLOSE_SESSION_REQUEST:
510 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
511 SessionClose( p_access, i_session_id );
524 #define AOT_NONE 0x000000
525 #define AOT_PROFILE_ENQ 0x9F8010
526 #define AOT_PROFILE 0x9F8011
527 #define AOT_PROFILE_CHANGE 0x9F8012
528 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
529 #define AOT_APPLICATION_INFO 0x9F8021
530 #define AOT_ENTER_MENU 0x9F8022
531 #define AOT_CA_INFO_ENQ 0x9F8030
532 #define AOT_CA_INFO 0x9F8031
533 #define AOT_CA_PMT 0x9F8032
534 #define AOT_CA_PMT_REPLY 0x9F8033
535 #define AOT_TUNE 0x9F8400
536 #define AOT_REPLACE 0x9F8401
537 #define AOT_CLEAR_REPLACE 0x9F8402
538 #define AOT_ASK_RELEASE 0x9F8403
539 #define AOT_DATE_TIME_ENQ 0x9F8440
540 #define AOT_DATE_TIME 0x9F8441
541 #define AOT_CLOSE_MMI 0x9F8800
542 #define AOT_DISPLAY_CONTROL 0x9F8801
543 #define AOT_DISPLAY_REPLY 0x9F8802
544 #define AOT_TEXT_LAST 0x9F8803
545 #define AOT_TEXT_MORE 0x9F8804
546 #define AOT_KEYPAD_CONTROL 0x9F8805
547 #define AOT_KEYPRESS 0x9F8806
548 #define AOT_ENQ 0x9F8807
549 #define AOT_ANSW 0x9F8808
550 #define AOT_MENU_LAST 0x9F8809
551 #define AOT_MENU_MORE 0x9F880A
552 #define AOT_MENU_ANSW 0x9F880B
553 #define AOT_LIST_LAST 0x9F880C
554 #define AOT_LIST_MORE 0x9F880D
555 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
556 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
557 #define AOT_DISPLAY_MESSAGE 0x9F8810
558 #define AOT_SCENE_END_MARK 0x9F8811
559 #define AOT_SCENE_DONE 0x9F8812
560 #define AOT_SCENE_CONTROL 0x9F8813
561 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
562 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
563 #define AOT_FLUSH_DOWNLOAD 0x9F8816
564 #define AOT_DOWNLOAD_REPLY 0x9F8817
565 #define AOT_COMMS_CMD 0x9F8C00
566 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
567 #define AOT_COMMS_REPLY 0x9F8C02
568 #define AOT_COMMS_SEND_LAST 0x9F8C03
569 #define AOT_COMMS_SEND_MORE 0x9F8C04
570 #define AOT_COMMS_RCV_LAST 0x9F8C05
571 #define AOT_COMMS_RCV_MORE 0x9F8C06
573 /*****************************************************************************
575 *****************************************************************************/
576 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
581 for ( i = 0; i < 3; i++ )
582 t = (t << 8) | *p_apdu++;
589 /*****************************************************************************
591 *****************************************************************************/
592 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
594 return GetLength( &p_apdu[3], pi_size );
597 /*****************************************************************************
599 *****************************************************************************/
600 static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
601 uint8_t *p_data, int i_size )
603 uint8_t *p_apdu = malloc( i_size + 12 );
607 *p++ = (i_tag >> 16);
608 *p++ = (i_tag >> 8) & 0xff;
610 p = SetLength( p, i_size );
612 memcpy( p, p_data, i_size );
614 i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
619 /*****************************************************************************
620 * ResourceManagerHandle
621 *****************************************************************************/
622 static void ResourceManagerHandle( access_t * p_access, int i_session_id,
623 uint8_t *p_apdu, int i_size )
625 int i_tag = APDUGetTag( p_apdu, i_size );
629 case AOT_PROFILE_ENQ:
631 int resources[] = { htonl(RI_RESOURCE_MANAGER),
632 htonl(RI_APPLICATION_INFORMATION),
633 htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
637 APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
642 APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
646 msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
651 /*****************************************************************************
652 * ResourceManagerOpen
653 *****************************************************************************/
654 static void ResourceManagerOpen( access_t * p_access, int i_session_id )
656 access_sys_t *p_sys = p_access->p_sys;
658 msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
660 p_sys->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
662 APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
665 /*****************************************************************************
666 * ApplicationInformationHandle
667 *****************************************************************************/
668 static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
669 uint8_t *p_apdu, int i_size )
671 int i_tag = APDUGetTag( p_apdu, i_size );
675 case AOT_APPLICATION_INFO:
677 int i_type, i_manufacturer, i_code;
679 uint8_t *d = APDUGetLength( p_apdu, &l );
682 p_apdu[l + 3] = '\0';
685 i_manufacturer = ((int)d[0] << 8) | d[1];
687 i_code = ((int)d[0] << 8) | d[1];
689 d = GetLength( d, &l );
691 msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
692 d, i_type, i_manufacturer, i_code );
697 "unexpected tag in ApplicationInformationHandle (0x%x)",
702 /*****************************************************************************
703 * ApplicationInformationOpen
704 *****************************************************************************/
705 static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
707 access_sys_t *p_sys = p_access->p_sys;
709 msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
711 p_sys->p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
713 APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
716 /*****************************************************************************
717 * ConditionalAccessHandle
718 *****************************************************************************/
719 static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
720 uint8_t *p_apdu, int i_size )
722 access_sys_t *p_sys = p_access->p_sys;
723 int i_tag = APDUGetTag( p_apdu, i_size );
729 if ( p_sys->i_nb_capmts )
732 msg_Dbg( p_access, "sending CAPMT on session %d", i_session_id );
733 for ( i = 0; i < p_sys->i_nb_capmts; i++ )
737 p = GetLength( &p_sys->pp_capmts[i][3], &i_size );
738 SPDUSend( p_access, i_session_id, p_sys->pp_capmts[i],
739 i_size + (p - p_sys->pp_capmts[i]) );
742 p_sys->i_ca_timeout = 100000;
748 "unexpected tag in ConditionalAccessHandle (0x%x)",
753 /*****************************************************************************
754 * ConditionalAccessOpen
755 *****************************************************************************/
756 static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
758 access_sys_t *p_sys = p_access->p_sys;
760 msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
762 p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
764 APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
773 /*****************************************************************************
775 *****************************************************************************/
776 static void DateTimeSend( access_t * p_access, int i_session_id )
778 access_sys_t *p_sys = p_access->p_sys;
779 date_time_t *p_date =
780 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
782 time_t t = time(NULL);
786 if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
788 int Y = tm_gmt.tm_year;
789 int M = tm_gmt.tm_mon + 1;
790 int D = tm_gmt.tm_mday;
791 int L = (M == 1 || M == 2) ? 1 : 0;
792 int MJD = 14956 + D + (int)((Y - L) * 365.25)
793 + (int)((M + 1 + L * 12) * 30.6001);
794 uint8_t p_response[7];
796 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
798 p_response[0] = htons(MJD) >> 8;
799 p_response[1] = htons(MJD) & 0xff;
800 p_response[2] = DEC2BCD(tm_gmt.tm_hour);
801 p_response[3] = DEC2BCD(tm_gmt.tm_min);
802 p_response[4] = DEC2BCD(tm_gmt.tm_sec);
803 p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
804 p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
806 APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
808 p_date->i_last = mdate();
812 /*****************************************************************************
814 *****************************************************************************/
815 static void DateTimeHandle( access_t * p_access, int i_session_id,
816 uint8_t *p_apdu, int i_size )
818 access_sys_t *p_sys = p_access->p_sys;
819 date_time_t *p_date =
820 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
822 int i_tag = APDUGetTag( p_apdu, i_size );
826 case AOT_DATE_TIME_ENQ:
829 const uint8_t *d = APDUGetLength( p_apdu, &l );
833 p_date->i_interval = *d;
834 msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
835 p_date->i_interval );
838 p_date->i_interval = 0;
840 DateTimeSend( p_access, i_session_id );
844 msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
848 /*****************************************************************************
850 *****************************************************************************/
851 static void DateTimeManage( access_t * p_access, int i_session_id )
853 access_sys_t *p_sys = p_access->p_sys;
854 date_time_t *p_date =
855 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
857 if ( p_date->i_interval
858 && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
860 DateTimeSend( p_access, i_session_id );
864 /*****************************************************************************
866 *****************************************************************************/
867 static void DateTimeOpen( access_t * p_access, int i_session_id )
869 access_sys_t *p_sys = p_access->p_sys;
871 msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
873 p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
874 p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
875 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t));
876 memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
878 DateTimeSend( p_access, i_session_id );
881 /*****************************************************************************
883 *****************************************************************************/
884 static void MMIHandle( access_t * p_access, int i_session_id,
885 uint8_t *p_apdu, int i_size )
887 int i_tag = APDUGetTag( p_apdu, i_size );
892 msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
896 /*****************************************************************************
898 *****************************************************************************/
899 static void MMIOpen( access_t * p_access, int i_session_id )
901 access_sys_t *p_sys = p_access->p_sys;
903 msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
905 p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
910 * External entry points
913 /*****************************************************************************
914 * en50221_Init : Open the transport layer
915 *****************************************************************************/
916 #define MAX_TC_RETRIES 20
918 int E_(en50221_Init)( access_t * p_access )
920 access_sys_t *p_sys = p_access->p_sys;
921 int i_slot, i_active_slots = 0;
923 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
926 if ( !p_sys->pb_active_slot[i_slot] )
928 p_sys->pb_active_slot[i_slot] = VLC_FALSE;
930 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
933 msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
938 /* This is out of the spec */
939 for ( i = 0; i < MAX_TC_RETRIES; i++ )
942 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
943 && i_tag == T_CTC_REPLY )
945 p_sys->pb_active_slot[i_slot] = VLC_TRUE;
950 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
954 "en50221_Init: couldn't send TPDU on slot %d",
960 p_sys->i_ca_timeout = 1000;
962 return i_active_slots;
965 /*****************************************************************************
966 * en50221_Poll : Poll the CAM for TPDUs
967 *****************************************************************************/
968 int E_(en50221_Poll)( access_t * p_access )
970 access_sys_t *p_sys = p_access->p_sys;
974 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
978 if ( !p_sys->pb_active_slot[i_slot] )
981 if ( !p_sys->pb_tc_has_data[i_slot] )
983 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, NULL, 0 ) !=
987 "en50221_Poll: couldn't send TPDU on slot %d",
991 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) !=
995 "en50221_Poll: couldn't recv TPDU on slot %d",
1001 while ( p_sys->pb_tc_has_data[i_slot] )
1003 uint8_t p_tpdu[MAX_TPDU_SIZE];
1004 int i_size, i_session_size;
1007 if ( TPDUSend( p_access, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
1010 "en50221_Poll: couldn't send TPDU on slot %d",
1014 if ( TPDURecv( p_access, i_slot, &i_tag, p_tpdu, &i_size ) !=
1018 "en50221_Poll: couldn't recv TPDU on slot %d",
1023 p_session = GetLength( &p_tpdu[3], &i_session_size );
1024 if ( i_session_size <= 1 )
1030 if ( i_tag != T_DATA_LAST )
1033 "en50221_Poll: fragmented TPDU not supported" );
1037 SPDUHandle( p_access, i_slot, p_session, i_session_size );
1041 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
1043 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1044 && p_sys->p_sessions[i_session_id - 1].pf_manage )
1046 p_sys->p_sessions[i_session_id - 1].pf_manage( p_access,
1055 /*****************************************************************************
1056 * en50221_SetCAPMT :
1057 *****************************************************************************/
1058 int E_(en50221_SetCAPMT)( access_t * p_access, uint8_t **pp_capmts,
1061 access_sys_t *p_sys = p_access->p_sys;
1064 for ( i_session_id = 0; i_session_id < MAX_SESSIONS; i_session_id++ )
1068 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1069 != RI_CONDITIONAL_ACCESS_SUPPORT )
1072 msg_Dbg( p_access, "sending CAPMT on session %d", i_session_id );
1073 for ( i = 0; i < i_nb_capmts; i++ )
1077 p = GetLength( &pp_capmts[i][3], &i_size );
1078 SPDUSend( p_access, i_session_id, pp_capmts[i],
1079 i_size + (p - pp_capmts[i]) );
1082 p_sys->i_ca_timeout = 100000;
1085 if ( p_sys->i_nb_capmts )
1088 for ( i = 0; i < p_sys->i_nb_capmts; i++ )
1090 free( p_sys->pp_capmts[i] );
1092 free( p_sys->pp_capmts );
1094 p_sys->pp_capmts = pp_capmts;
1095 p_sys->i_nb_capmts = i_nb_capmts;
1100 /*****************************************************************************
1102 *****************************************************************************/
1103 void E_(en50221_End)( access_t * p_access )
1105 access_sys_t *p_sys = p_access->p_sys;
1107 if ( p_sys->i_nb_capmts )
1110 for ( i = 0; i < p_sys->i_nb_capmts; i++ )
1112 free( p_sys->pp_capmts[i] );
1114 free( p_sys->pp_capmts );