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>
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>
49 static void ResourceManagerOpen( access_t * p_access, int i_session_id );
50 static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
51 static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
52 static void DateTimeOpen( access_t * p_access, int i_session_id );
53 static void MMIOpen( access_t * p_access, int i_session_id );
55 /*****************************************************************************
57 *****************************************************************************/
58 #define SIZE_INDICATOR 0x80
60 static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
62 *pi_length = *p_data++;
64 if ( (*pi_length & SIZE_INDICATOR) != 0 )
66 int l = *pi_length & ~SIZE_INDICATOR;
70 for ( i = 0; i < l; i++ )
71 *pi_length = (*pi_length << 8) | *p_data++;
77 static uint8_t *SetLength( uint8_t *p_data, int i_length )
85 else if ( i_length < 256 )
87 *p++ = SIZE_INDICATOR | 0x1;
90 else if ( i_length < 65536 )
92 *p++ = SIZE_INDICATOR | 0x2;
94 *p++ = i_length & 0xff;
96 else if ( i_length < 16777216 )
98 *p++ = SIZE_INDICATOR | 0x3;
99 *p++ = i_length >> 16;
100 *p++ = (i_length >> 8) & 0xff;
101 *p++ = i_length & 0xff;
105 *p++ = SIZE_INDICATOR | 0x4;
106 *p++ = i_length >> 24;
107 *p++ = (i_length >> 16) & 0xff;
108 *p++ = (i_length >> 8) & 0xff;
109 *p++ = i_length & 0xff;
120 #define MAX_TPDU_SIZE 2048
121 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
123 #define DATA_INDICATOR 0x80
127 #define T_CREATE_TC 0x82
128 #define T_CTC_REPLY 0x83
129 #define T_DELETE_TC 0x84
130 #define T_DTC_REPLY 0x85
131 #define T_REQUEST_TC 0x86
132 #define T_NEW_TC 0x87
133 #define T_TC_ERROR 0x88
134 #define T_DATA_LAST 0xA0
135 #define T_DATA_MORE 0xA1
137 static void Dump( vlc_bool_t b_outgoing, uint8_t *p_data, int i_size )
142 fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
143 for ( i = 0; i < i_size && i < MAX_DUMP; i++)
144 fprintf(stderr, "%02X ", p_data[i]);
145 fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
149 /*****************************************************************************
151 *****************************************************************************/
152 static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
153 const uint8_t *p_content, int i_length )
155 access_sys_t *p_sys = p_access->p_sys;
156 uint8_t i_tcid = i_slot + 1;
157 uint8_t p_data[MAX_TPDU_SIZE];
173 p_data[3] = 1; /* length */
180 p_data[3] = 2; /* length */
182 p_data[5] = p_content[0];
189 /* i_length <= MAX_TPDU_DATA */
190 uint8_t *p = p_data + 3;
191 p = SetLength( p, i_length + 1 );
195 memcpy( p, p_content, i_length );
196 i_size = i_length + (p - p_data);
203 Dump( VLC_TRUE, p_data, i_size );
205 if ( write( p_sys->i_ca_handle, p_data, i_size ) != i_size )
207 msg_Err( p_access, "cannot write to CAM device (%s)",
216 /*****************************************************************************
218 *****************************************************************************/
219 #define CAM_READ_TIMEOUT 3500 // ms
221 static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
222 uint8_t *p_data, int *pi_size )
224 access_sys_t *p_sys = p_access->p_sys;
225 uint8_t i_tcid = i_slot + 1;
227 struct pollfd pfd[1];
229 pfd[0].fd = p_sys->i_ca_handle;
230 pfd[0].events = POLLIN;
231 if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
233 msg_Err( p_access, "cannot poll from CAM device" );
237 if ( pi_size == NULL )
239 p_data = malloc( MAX_TPDU_SIZE );
244 i_size = read( p_sys->i_ca_handle, p_data, MAX_TPDU_SIZE );
246 if ( i_size >= 0 || errno != EINTR )
252 msg_Err( p_access, "cannot read from CAM device (%d:%s)", i_size,
257 if ( p_data[1] != i_tcid )
259 msg_Err( p_access, "invalid read from CAM device (%d instead of %d)",
265 p_sys->pb_tc_has_data[i_slot] = (i_size >= 4
266 && p_data[i_size - 4] == T_SB
267 && p_data[i_size - 3] == 2
268 && (p_data[i_size - 1] & DATA_INDICATOR))
269 ? VLC_TRUE : VLC_FALSE;
271 Dump( VLC_FALSE, p_data, i_size );
273 if ( pi_size == NULL )
286 #define ST_SESSION_NUMBER 0x90
287 #define ST_OPEN_SESSION_REQUEST 0x91
288 #define ST_OPEN_SESSION_RESPONSE 0x92
289 #define ST_CREATE_SESSION 0x93
290 #define ST_CREATE_SESSION_RESPONSE 0x94
291 #define ST_CLOSE_SESSION_REQUEST 0x95
292 #define ST_CLOSE_SESSION_RESPONSE 0x96
295 #define SS_NOT_ALLOCATED 0xF0
297 #define RI_RESOURCE_MANAGER 0x00010041
298 #define RI_APPLICATION_INFORMATION 0x00020041
299 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
300 #define RI_HOST_CONTROL 0x00200041
301 #define RI_DATE_TIME 0x00240041
302 #define RI_MMI 0x00400041
304 static int ResourceIdToInt( uint8_t *p_data )
306 return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
307 | ((int)p_data[2] << 8) | p_data[3];
310 /*****************************************************************************
312 *****************************************************************************/
313 static int SPDUSend( access_t * p_access, int i_session_id,
314 uint8_t *p_data, int i_size )
316 access_sys_t *p_sys = p_access->p_sys;
317 uint8_t *p_spdu = malloc( i_size + 4 );
320 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
322 *p++ = ST_SESSION_NUMBER;
324 *p++ = (i_session_id >> 8);
325 *p++ = i_session_id & 0xff;
327 memcpy( p, p_data, i_size );
334 if ( i_size > MAX_TPDU_DATA )
336 if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
337 MAX_TPDU_DATA ) != VLC_SUCCESS )
339 msg_Err( p_access, "couldn't send TPDU on session %d",
345 i_size -= MAX_TPDU_DATA;
349 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
352 msg_Err( p_access, "couldn't send TPDU on session %d",
360 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
363 msg_Err( p_access, "couldn't recv TPDU on session %d",
374 /*****************************************************************************
376 *****************************************************************************/
377 static void SessionOpen( access_t * p_access, uint8_t i_slot,
378 uint8_t *p_spdu, int i_size )
380 access_sys_t *p_sys = p_access->p_sys;
382 int i_resource_id = ResourceIdToInt( &p_spdu[2] );
383 uint8_t p_response[16];
384 int i_status = SS_NOT_ALLOCATED;
387 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
389 if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
392 if ( i_session_id == MAX_SESSIONS )
394 msg_Err( p_access, "too many sessions !" );
397 p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
398 p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
399 p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
400 p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
402 if ( i_resource_id == RI_RESOURCE_MANAGER
403 || i_resource_id == RI_APPLICATION_INFORMATION
404 || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
405 || i_resource_id == RI_DATE_TIME
406 || i_resource_id == RI_MMI )
411 p_response[0] = ST_OPEN_SESSION_RESPONSE;
413 p_response[2] = i_status;
414 p_response[3] = p_spdu[2];
415 p_response[4] = p_spdu[3];
416 p_response[5] = p_spdu[4];
417 p_response[6] = p_spdu[5];
418 p_response[7] = i_session_id >> 8;
419 p_response[8] = i_session_id & 0xff;
421 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) !=
425 "SessionOpen: couldn't send TPDU on slot %d", i_slot );
428 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
431 "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
435 switch ( i_resource_id )
437 case RI_RESOURCE_MANAGER:
438 ResourceManagerOpen( p_access, i_session_id ); break;
439 case RI_APPLICATION_INFORMATION:
440 ApplicationInformationOpen( p_access, i_session_id ); break;
441 case RI_CONDITIONAL_ACCESS_SUPPORT:
442 ConditionalAccessOpen( p_access, i_session_id ); break;
444 DateTimeOpen( p_access, i_session_id ); break;
446 MMIOpen( p_access, i_session_id ); break;
448 case RI_HOST_CONTROL:
450 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
451 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
455 /*****************************************************************************
457 *****************************************************************************/
458 static void SessionClose( access_t * p_access, int i_session_id )
460 access_sys_t *p_sys = p_access->p_sys;
461 uint8_t p_response[16];
463 uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
465 if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
466 p_sys->p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
467 p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
469 p_response[0] = ST_CLOSE_SESSION_RESPONSE;
471 p_response[2] = SS_OK;
472 p_response[3] = i_session_id >> 8;
473 p_response[4] = i_session_id & 0xff;
475 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
479 "SessionOpen: couldn't send TPDU on slot %d", i_slot );
482 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
485 "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
490 /*****************************************************************************
492 *****************************************************************************/
493 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
494 uint8_t *p_spdu, int i_size )
496 access_sys_t *p_sys = p_access->p_sys;
501 case ST_SESSION_NUMBER:
504 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
505 p_sys->p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
506 p_spdu + 4, i_size - 4 );
509 case ST_OPEN_SESSION_REQUEST:
510 if ( i_size != 6 || p_spdu[1] != 0x4 )
512 SessionOpen( p_access, i_slot, p_spdu, i_size );
515 case ST_CLOSE_SESSION_REQUEST:
516 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
517 SessionClose( p_access, i_session_id );
530 #define AOT_NONE 0x000000
531 #define AOT_PROFILE_ENQ 0x9F8010
532 #define AOT_PROFILE 0x9F8011
533 #define AOT_PROFILE_CHANGE 0x9F8012
534 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
535 #define AOT_APPLICATION_INFO 0x9F8021
536 #define AOT_ENTER_MENU 0x9F8022
537 #define AOT_CA_INFO_ENQ 0x9F8030
538 #define AOT_CA_INFO 0x9F8031
539 #define AOT_CA_PMT 0x9F8032
540 #define AOT_CA_PMT_REPLY 0x9F8033
541 #define AOT_TUNE 0x9F8400
542 #define AOT_REPLACE 0x9F8401
543 #define AOT_CLEAR_REPLACE 0x9F8402
544 #define AOT_ASK_RELEASE 0x9F8403
545 #define AOT_DATE_TIME_ENQ 0x9F8440
546 #define AOT_DATE_TIME 0x9F8441
547 #define AOT_CLOSE_MMI 0x9F8800
548 #define AOT_DISPLAY_CONTROL 0x9F8801
549 #define AOT_DISPLAY_REPLY 0x9F8802
550 #define AOT_TEXT_LAST 0x9F8803
551 #define AOT_TEXT_MORE 0x9F8804
552 #define AOT_KEYPAD_CONTROL 0x9F8805
553 #define AOT_KEYPRESS 0x9F8806
554 #define AOT_ENQ 0x9F8807
555 #define AOT_ANSW 0x9F8808
556 #define AOT_MENU_LAST 0x9F8809
557 #define AOT_MENU_MORE 0x9F880A
558 #define AOT_MENU_ANSW 0x9F880B
559 #define AOT_LIST_LAST 0x9F880C
560 #define AOT_LIST_MORE 0x9F880D
561 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
562 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
563 #define AOT_DISPLAY_MESSAGE 0x9F8810
564 #define AOT_SCENE_END_MARK 0x9F8811
565 #define AOT_SCENE_DONE 0x9F8812
566 #define AOT_SCENE_CONTROL 0x9F8813
567 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
568 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
569 #define AOT_FLUSH_DOWNLOAD 0x9F8816
570 #define AOT_DOWNLOAD_REPLY 0x9F8817
571 #define AOT_COMMS_CMD 0x9F8C00
572 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
573 #define AOT_COMMS_REPLY 0x9F8C02
574 #define AOT_COMMS_SEND_LAST 0x9F8C03
575 #define AOT_COMMS_SEND_MORE 0x9F8C04
576 #define AOT_COMMS_RCV_LAST 0x9F8C05
577 #define AOT_COMMS_RCV_MORE 0x9F8C06
579 /*****************************************************************************
581 *****************************************************************************/
582 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
587 for ( i = 0; i < 3; i++ )
588 t = (t << 8) | *p_apdu++;
595 /*****************************************************************************
597 *****************************************************************************/
598 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
600 return GetLength( &p_apdu[3], pi_size );
603 /*****************************************************************************
605 *****************************************************************************/
606 static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
607 uint8_t *p_data, int i_size )
609 uint8_t *p_apdu = malloc( i_size + 12 );
613 *p++ = (i_tag >> 16);
614 *p++ = (i_tag >> 8) & 0xff;
616 p = SetLength( p, i_size );
618 memcpy( p, p_data, i_size );
620 i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
625 /*****************************************************************************
626 * ResourceManagerHandle
627 *****************************************************************************/
628 static void ResourceManagerHandle( access_t * p_access, int i_session_id,
629 uint8_t *p_apdu, int i_size )
631 int i_tag = APDUGetTag( p_apdu, i_size );
635 case AOT_PROFILE_ENQ:
637 int resources[] = { htonl(RI_RESOURCE_MANAGER),
638 htonl(RI_APPLICATION_INFORMATION),
639 htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
643 APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
648 APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
652 msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
657 /*****************************************************************************
658 * ResourceManagerOpen
659 *****************************************************************************/
660 static void ResourceManagerOpen( access_t * p_access, int i_session_id )
662 access_sys_t *p_sys = p_access->p_sys;
664 msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
666 p_sys->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
668 APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
671 /*****************************************************************************
672 * ApplicationInformationHandle
673 *****************************************************************************/
674 static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
675 uint8_t *p_apdu, int i_size )
677 int i_tag = APDUGetTag( p_apdu, i_size );
681 case AOT_APPLICATION_INFO:
683 int i_type, i_manufacturer, i_code;
685 uint8_t *d = APDUGetLength( p_apdu, &l );
688 p_apdu[l + 3] = '\0';
691 i_manufacturer = ((int)d[0] << 8) | d[1];
693 i_code = ((int)d[0] << 8) | d[1];
695 d = GetLength( d, &l );
697 msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
698 d, i_type, i_manufacturer, i_code );
703 "unexpected tag in ApplicationInformationHandle (0x%x)",
708 /*****************************************************************************
709 * ApplicationInformationOpen
710 *****************************************************************************/
711 static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
713 access_sys_t *p_sys = p_access->p_sys;
715 msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
717 p_sys->p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
719 APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
722 /*****************************************************************************
723 * ConditionalAccessHandle
724 *****************************************************************************/
725 static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
726 uint8_t *p_apdu, int i_size )
728 access_sys_t *p_sys = p_access->p_sys;
729 int i_tag = APDUGetTag( p_apdu, i_size );
735 if ( p_sys->i_nb_capmts )
738 msg_Dbg( p_access, "sending CAPMT on session %d", i_session_id );
739 for ( i = 0; i < p_sys->i_nb_capmts; i++ )
743 p = GetLength( &p_sys->pp_capmts[i][3], &i_size );
744 SPDUSend( p_access, i_session_id, p_sys->pp_capmts[i],
745 i_size + (p - p_sys->pp_capmts[i]) );
748 p_sys->i_ca_timeout = 100000;
754 "unexpected tag in ConditionalAccessHandle (0x%x)",
759 /*****************************************************************************
760 * ConditionalAccessOpen
761 *****************************************************************************/
762 static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
764 access_sys_t *p_sys = p_access->p_sys;
766 msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
768 p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
770 APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
779 /*****************************************************************************
781 *****************************************************************************/
782 static void DateTimeSend( access_t * p_access, int i_session_id )
784 access_sys_t *p_sys = p_access->p_sys;
785 date_time_t *p_date =
786 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
788 time_t t = time(NULL);
792 if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
794 int Y = tm_gmt.tm_year;
795 int M = tm_gmt.tm_mon + 1;
796 int D = tm_gmt.tm_mday;
797 int L = (M == 1 || M == 2) ? 1 : 0;
798 int MJD = 14956 + D + (int)((Y - L) * 365.25)
799 + (int)((M + 1 + L * 12) * 30.6001);
800 uint8_t p_response[7];
802 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
804 p_response[0] = htons(MJD) >> 8;
805 p_response[1] = htons(MJD) & 0xff;
806 p_response[2] = DEC2BCD(tm_gmt.tm_hour);
807 p_response[3] = DEC2BCD(tm_gmt.tm_min);
808 p_response[4] = DEC2BCD(tm_gmt.tm_sec);
809 p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
810 p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
812 APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
814 p_date->i_last = mdate();
818 /*****************************************************************************
820 *****************************************************************************/
821 static void DateTimeHandle( access_t * p_access, int i_session_id,
822 uint8_t *p_apdu, int i_size )
824 access_sys_t *p_sys = p_access->p_sys;
825 date_time_t *p_date =
826 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
828 int i_tag = APDUGetTag( p_apdu, i_size );
832 case AOT_DATE_TIME_ENQ:
835 const uint8_t *d = APDUGetLength( p_apdu, &l );
839 p_date->i_interval = *d;
840 msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
841 p_date->i_interval );
844 p_date->i_interval = 0;
846 DateTimeSend( p_access, i_session_id );
850 msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
854 /*****************************************************************************
856 *****************************************************************************/
857 static void DateTimeManage( access_t * p_access, int i_session_id )
859 access_sys_t *p_sys = p_access->p_sys;
860 date_time_t *p_date =
861 (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
863 if ( p_date->i_interval
864 && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
866 DateTimeSend( p_access, i_session_id );
870 /*****************************************************************************
872 *****************************************************************************/
873 static void DateTimeOpen( access_t * p_access, int i_session_id )
875 access_sys_t *p_sys = p_access->p_sys;
877 msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
879 p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
880 p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
881 p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t));
882 memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
884 DateTimeSend( p_access, i_session_id );
887 /*****************************************************************************
889 *****************************************************************************/
890 static void MMIHandle( access_t * p_access, int i_session_id,
891 uint8_t *p_apdu, int i_size )
893 int i_tag = APDUGetTag( p_apdu, i_size );
898 msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
902 /*****************************************************************************
904 *****************************************************************************/
905 static void MMIOpen( access_t * p_access, int i_session_id )
907 access_sys_t *p_sys = p_access->p_sys;
909 msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
911 p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
919 /*****************************************************************************
920 * InitSlot: Open the transport layer
921 *****************************************************************************/
922 #define MAX_TC_RETRIES 20
924 static int InitSlot( access_t * p_access, int i_slot )
926 access_sys_t *p_sys = p_access->p_sys;
929 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
932 msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
937 /* This is out of the spec */
938 for ( i = 0; i < MAX_TC_RETRIES; i++ )
941 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
942 && i_tag == T_CTC_REPLY )
944 p_sys->pb_active_slot[i_slot] = VLC_TRUE;
948 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
952 "en50221_Init: couldn't send TPDU on slot %d",
957 if ( p_sys->pb_active_slot[i_slot] )
959 p_sys->i_ca_timeout = 1000;
968 * External entry points
971 /*****************************************************************************
972 * en50221_Poll : Poll the CAM for TPDUs
973 *****************************************************************************/
974 int E_(en50221_Poll)( access_t * p_access )
976 access_sys_t *p_sys = p_access->p_sys;
980 for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
984 if ( !p_sys->pb_active_slot[i_slot] )
986 ca_slot_info_t sinfo;
988 if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
990 msg_Err( p_access, "en50221_Poll: couldn't get info on slot %d",
995 if ( sinfo.flags & CA_CI_MODULE_READY )
997 msg_Dbg( p_access, "en50221_Poll: slot %d is active",
999 p_sys->pb_active_slot[i_slot] = VLC_TRUE;
1004 InitSlot( p_access, i_slot );
1007 if ( !p_sys->pb_tc_has_data[i_slot] )
1009 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, NULL, 0 ) !=
1013 "en50221_Poll: couldn't send TPDU on slot %d",
1017 if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) !=
1021 "en50221_Poll: couldn't recv TPDU on slot %d",
1027 while ( p_sys->pb_tc_has_data[i_slot] )
1029 uint8_t p_tpdu[MAX_TPDU_SIZE];
1030 int i_size, i_session_size;
1033 if ( TPDUSend( p_access, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
1036 "en50221_Poll: couldn't send TPDU on slot %d",
1040 if ( TPDURecv( p_access, i_slot, &i_tag, p_tpdu, &i_size ) !=
1044 "en50221_Poll: couldn't recv TPDU on slot %d",
1049 p_session = GetLength( &p_tpdu[3], &i_session_size );
1050 if ( i_session_size <= 1 )
1056 if ( i_tag != T_DATA_LAST )
1059 "en50221_Poll: fragmented TPDU not supported" );
1063 SPDUHandle( p_access, i_slot, p_session, i_session_size );
1067 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
1069 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1070 && p_sys->p_sessions[i_session_id - 1].pf_manage )
1072 p_sys->p_sessions[i_session_id - 1].pf_manage( p_access,
1081 /*****************************************************************************
1082 * en50221_SetCAPMT :
1083 *****************************************************************************/
1084 int E_(en50221_SetCAPMT)( access_t * p_access, uint8_t **pp_capmts,
1087 access_sys_t *p_sys = p_access->p_sys;
1090 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
1094 if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1095 != RI_CONDITIONAL_ACCESS_SUPPORT )
1098 msg_Dbg( p_access, "sending CAPMT on session %d", i_session_id );
1099 for ( i = 0; i < i_nb_capmts; i++ )
1103 p = GetLength( &pp_capmts[i][3], &i_size );
1104 SPDUSend( p_access, i_session_id, pp_capmts[i],
1105 i_size + (p - pp_capmts[i]) );
1108 p_sys->i_ca_timeout = 100000;
1111 if ( p_sys->i_nb_capmts )
1114 for ( i = 0; i < p_sys->i_nb_capmts; i++ )
1116 free( p_sys->pp_capmts[i] );
1118 free( p_sys->pp_capmts );
1120 p_sys->pp_capmts = pp_capmts;
1121 p_sys->i_nb_capmts = i_nb_capmts;
1126 /*****************************************************************************
1128 *****************************************************************************/
1129 void E_(en50221_End)( access_t * p_access )
1131 access_sys_t *p_sys = p_access->p_sys;
1133 if ( p_sys->i_nb_capmts )
1136 for ( i = 0; i < p_sys->i_nb_capmts; i++ )
1138 free( p_sys->pp_capmts[i] );
1140 free( p_sys->pp_capmts );