1 /*****************************************************************************
2 * en50221.c : implementation of the transport, session and applications
4 *****************************************************************************
5 * Copyright (C) 2004-2005, 2010, 2015 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
26 #ifdef HAVE_DVB_SUPPORT
36 #include <sys/types.h>
39 #include <sys/ioctl.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
48 /* DVB Card Drivers */
49 #include <linux/dvb/version.h>
50 #include <linux/dvb/dmx.h>
51 #include <linux/dvb/frontend.h>
52 #include <linux/dvb/ca.h>
54 #include <bitstream/mpeg/psi.h>
55 #include <bitstream/dvb/ci.h>
56 #include <bitstream/dvb/si.h>
62 #define TAB_APPEND( count, tab, p ) \
65 (tab) = realloc( tab, sizeof( void ** ) * ( (count) + 1 ) ); \
69 (tab) = malloc( sizeof( void ** ) ); \
74 /*****************************************************************************
76 *****************************************************************************/
78 #define CAM_INIT_TIMEOUT 15000000 /* 15 s */
79 #undef HLCI_WAIT_CAM_READY
80 #define CAPMT_WAIT 100 /* ms */
81 #define CA_POLL_PERIOD 100000 /* 100 ms */
83 typedef struct en50221_msg_t
87 struct en50221_msg_t
*p_next
;
90 typedef struct en50221_session_t
94 void (* pf_handle
)( access_t
*, int, uint8_t *, int );
95 void (* pf_close
)( access_t
*, int );
96 void (* pf_manage
)( access_t
*, int );
100 typedef struct ci_slot_t
103 bool b_expect_answer
;
106 bool b_mmi_undisplayed
;
109 en50221_msg_t
*p_recv
;
112 en50221_msg_t
*p_send
;
113 en50221_msg_t
**pp_send_last
;
117 struct ev_timer init_watcher
;
119 /* SPDUSend callback, if p_spdu is not NULL */
120 /* SessionOpen callback, if not 0 */
121 int i_pending_session_id
;
127 static struct ev_io cam_watcher
;
128 static struct ev_timer slot_watcher
;
129 static int i_nb_slots
= 0;
130 static ci_slot_t p_slots
[MAX_CI_SLOTS
];
131 static en50221_session_t p_sessions
[MAX_SESSIONS
];
133 /*****************************************************************************
135 *****************************************************************************/
136 static void SessionOpenCb( access_t
*p_access
, uint8_t i_slot
);
137 static void SPDUHandle( access_t
* p_access
, uint8_t i_slot
,
138 uint8_t *p_spdu
, int i_size
);
140 static void ResourceManagerOpen( access_t
* p_access
, int i_session_id
);
141 static void ApplicationInformationOpen( access_t
* p_access
, int i_session_id
);
142 static void ConditionalAccessOpen( access_t
* p_access
, int i_session_id
);
143 static void DateTimeOpen( access_t
* p_access
, int i_session_id
);
144 static void ResetSlotCb(struct ev_loop
*loop
, struct ev_timer
*w
, int revents
);
145 static void en50221_Read(struct ev_loop
*loop
, struct ev_io
*w
, int revents
);
146 static void en50221_Poll(struct ev_loop
*loop
, struct ev_timer
*w
, int revents
);
147 static void MMIOpen( access_t
* p_access
, int i_session_id
);
149 /*****************************************************************************
151 *****************************************************************************/
152 #define SIZE_INDICATOR 0x80
154 static uint8_t *GetLength( uint8_t *p_data
, int *pi_length
)
156 *pi_length
= *p_data
++;
158 if ( (*pi_length
& SIZE_INDICATOR
) != 0 )
160 int l
= *pi_length
& ~SIZE_INDICATOR
;
164 for ( i
= 0; i
< l
; i
++ )
165 *pi_length
= (*pi_length
<< 8) | *p_data
++;
171 static uint8_t *SetLength( uint8_t *p_data
, int i_length
)
175 if ( i_length
< 128 )
179 else if ( i_length
< 256 )
181 *p
++ = SIZE_INDICATOR
| 0x1;
184 else if ( i_length
< 65536 )
186 *p
++ = SIZE_INDICATOR
| 0x2;
187 *p
++ = i_length
>> 8;
188 *p
++ = i_length
& 0xff;
190 else if ( i_length
< 16777216 )
192 *p
++ = SIZE_INDICATOR
| 0x3;
193 *p
++ = i_length
>> 16;
194 *p
++ = (i_length
>> 8) & 0xff;
195 *p
++ = i_length
& 0xff;
199 *p
++ = SIZE_INDICATOR
| 0x4;
200 *p
++ = i_length
>> 24;
201 *p
++ = (i_length
>> 16) & 0xff;
202 *p
++ = (i_length
>> 8) & 0xff;
203 *p
++ = i_length
& 0xff;
214 #define MAX_TPDU_SIZE 4096
215 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 7)
217 #define DATA_INDICATOR 0x80
221 #define T_CREATE_TC 0x82
222 #define T_CTC_REPLY 0x83
223 #define T_DELETE_TC 0x84
224 #define T_DTC_REPLY 0x85
225 #define T_REQUEST_TC 0x86
226 #define T_NEW_TC 0x87
227 #define T_TC_ERROR 0x88
228 #define T_DATA_LAST 0xA0
229 #define T_DATA_MORE 0xA1
231 static void Dump( bool b_outgoing
, uint8_t *p_data
, int i_size
)
236 fprintf(stderr
, "%s ", b_outgoing
? "-->" : "<--");
237 for ( i
= 0; i
< i_size
&& i
< MAX_DUMP
; i
++)
238 fprintf(stderr
, "%02X ", p_data
[i
]);
239 fprintf(stderr
, "%s\n", i_size
>= MAX_DUMP
? "..." : "");
243 /*****************************************************************************
245 *****************************************************************************/
246 static int TPDUWrite( access_t
* p_access
, uint8_t i_slot
)
248 ci_slot_t
*p_slot
= &p_slots
[i_slot
];
249 en50221_msg_t
*p_send
= p_slot
->p_send
;
251 if ( p_slot
->b_expect_answer
)
253 "en50221: writing while expecting an answer on slot %u",
255 if ( p_send
== NULL
)
257 msg_Warn( p_access
, "en50221: no data to write on slot %u !", i_slot
);
260 p_slot
->p_send
= p_send
->p_next
;
261 if ( p_slot
->p_send
== NULL
)
262 p_slot
->pp_send_last
= &p_slot
->p_send
;
264 Dump( true, p_send
->p_data
, p_send
->i_size
);
266 if ( write( i_ca_handle
, p_send
->p_data
, p_send
->i_size
)
269 msg_Err( p_access
, "en50221: cannot write to CAM device (%m)" );
270 free( p_send
->p_data
);
275 free( p_send
->p_data
);
277 p_slot
->b_expect_answer
= true;
282 /*****************************************************************************
284 *****************************************************************************/
285 static int TPDUSend( access_t
* p_access
, uint8_t i_slot
, uint8_t i_tag
,
286 const uint8_t *p_content
, int i_length
)
288 ci_slot_t
*p_slot
= &p_slots
[i_slot
];
289 uint8_t i_tcid
= i_slot
+ 1;
290 en50221_msg_t
*p_send
= malloc( sizeof(en50221_msg_t
) );
291 uint8_t *p_data
= malloc( MAX_TPDU_SIZE
);
307 p_data
[3] = 1; /* length */
314 p_data
[3] = 2; /* length */
316 p_data
[5] = p_content
[0];
323 /* i_length <= MAX_TPDU_DATA */
324 uint8_t *p
= p_data
+ 3;
325 p
= SetLength( p
, i_length
+ 1 );
329 memcpy( p
, p_content
, i_length
);
330 i_size
= i_length
+ (p
- p_data
);
338 p_send
->p_data
= p_data
;
339 p_send
->i_size
= i_size
;
340 p_send
->p_next
= NULL
;
342 *p_slot
->pp_send_last
= p_send
;
343 p_slot
->pp_send_last
= &p_send
->p_next
;
345 if ( !p_slot
->b_expect_answer
)
346 return TPDUWrite( p_access
, i_slot
);
352 /*****************************************************************************
354 *****************************************************************************/
355 static int TPDURecv( access_t
* p_access
)
358 uint8_t i_tag
, i_slot
;
359 uint8_t p_data
[MAX_TPDU_SIZE
];
365 i_size
= read( i_ca_handle
, p_data
, MAX_TPDU_SIZE
);
367 while ( i_size
< 0 && errno
== EINTR
);
371 msg_Err( p_access
, "en50221: cannot read from CAM device (%d:%m)",
376 Dump( false, p_data
, i_size
);
378 i_slot
= p_data
[1] - 1;
381 if ( i_slot
>= i_nb_slots
)
383 msg_Warn( p_access
, "en50221: TPDU is from an unknown slot %u",
387 p_slot
= &p_slots
[i_slot
];
389 p_slot
->b_has_data
= !!(p_data
[i_size
- 4] == T_SB
390 && p_data
[i_size
- 3] == 2
391 && (p_data
[i_size
- 1] & DATA_INDICATOR
));
392 p_slot
->b_expect_answer
= false;
397 p_slot
->b_active
= true;
398 ev_timer_stop(event_loop
, &p_slot
->init_watcher
);
399 msg_Dbg( p_access
, "CI slot %d is active", i_slot
);
407 /* intended pass-through */
410 en50221_msg_t
*p_recv
;
412 uint8_t *p_session
= GetLength( &p_data
[3], &i_session_size
);
414 if ( i_session_size
<= 1 )
419 if ( p_slot
->p_recv
== NULL
)
421 p_slot
->p_recv
= malloc( sizeof(en50221_msg_t
) );
422 p_slot
->p_recv
->p_data
= NULL
;
423 p_slot
->p_recv
->i_size
= 0;
426 p_recv
= p_slot
->p_recv
;
427 p_recv
->p_data
= realloc( p_recv
->p_data
,
428 p_recv
->i_size
+ i_session_size
);
429 memcpy( &p_recv
->p_data
[ p_recv
->i_size
], p_session
, i_session_size
);
430 p_recv
->i_size
+= i_session_size
;
434 SPDUHandle( p_access
, i_slot
, p_recv
->p_data
, p_recv
->i_size
);
435 free( p_recv
->p_data
);
437 p_slot
->p_recv
= NULL
;
443 msg_Warn( p_access
, "en50221: unhandled R_TPDU tag %u slot %u", i_tag
,
448 if ( !p_slot
->b_expect_answer
&& p_slot
->p_send
!= NULL
)
449 TPDUWrite( p_access
, i_slot
);
450 if ( !p_slot
->b_expect_answer
&& p_slot
->i_pending_session_id
!= 0 )
451 SessionOpenCb( p_access
, i_slot
);
452 if ( !p_slot
->b_expect_answer
&& p_slot
->b_has_data
)
453 TPDUSend( p_access
, i_slot
, T_RCV
, NULL
, 0 );
463 #define ST_SESSION_NUMBER 0x90
464 #define ST_OPEN_SESSION_REQUEST 0x91
465 #define ST_OPEN_SESSION_RESPONSE 0x92
466 #define ST_CREATE_SESSION 0x93
467 #define ST_CREATE_SESSION_RESPONSE 0x94
468 #define ST_CLOSE_SESSION_REQUEST 0x95
469 #define ST_CLOSE_SESSION_RESPONSE 0x96
472 #define SS_NOT_ALLOCATED 0xF0
474 #define RI_RESOURCE_MANAGER 0x00010041
475 #define RI_APPLICATION_INFORMATION 0x00020041
476 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
477 #define RI_HOST_CONTROL 0x00200041
478 #define RI_DATE_TIME 0x00240041
479 #define RI_MMI 0x00400041
481 static int ResourceIdToInt( uint8_t *p_data
)
483 return ((int)p_data
[0] << 24) | ((int)p_data
[1] << 16)
484 | ((int)p_data
[2] << 8) | p_data
[3];
487 /*****************************************************************************
489 *****************************************************************************/
490 static int SPDUSend( access_t
*p_access
, int i_session_id
,
491 uint8_t *p_data
, int i_size
)
493 uint8_t *p_spdu
= malloc( i_size
+ 4 );
495 uint8_t i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
497 *p
++ = ST_SESSION_NUMBER
;
499 *p
++ = (i_session_id
>> 8);
500 *p
++ = i_session_id
& 0xff;
502 memcpy( p
, p_data
, i_size
);
509 if ( i_size
> MAX_TPDU_DATA
)
511 if ( TPDUSend( p_access
, i_slot
, T_DATA_MORE
, p
,
512 MAX_TPDU_DATA
) != 0 )
514 msg_Err( p_access
, "couldn't send TPDU on session %d",
520 i_size
-= MAX_TPDU_DATA
;
524 if ( TPDUSend( p_access
, i_slot
, T_DATA_LAST
, p
, i_size
)
527 msg_Err( p_access
, "couldn't send TPDU on session %d",
540 /*****************************************************************************
542 *****************************************************************************/
543 static void SessionOpenCb( access_t
*p_access
, uint8_t i_slot
)
545 ci_slot_t
*p_slot
= &p_slots
[i_slot
];
546 int i_session_id
= p_slot
->i_pending_session_id
;
547 int i_resource_id
= p_sessions
[i_session_id
- 1].i_resource_id
;
549 p_slot
->i_pending_session_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_sessions
[i_session_id
- 1].i_resource_id
= 0;
571 static void SessionOpen( access_t
* p_access
, uint8_t i_slot
,
572 uint8_t *p_spdu
, int i_size
)
574 ci_slot_t
*p_slot
= &p_slots
[i_slot
];
576 int i_resource_id
= ResourceIdToInt( &p_spdu
[2] );
577 uint8_t p_response
[16];
578 int i_status
= SS_NOT_ALLOCATED
;
580 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
582 if ( !p_sessions
[i_session_id
- 1].i_resource_id
)
585 if ( i_session_id
> MAX_SESSIONS
)
587 msg_Err( p_access
, "too many sessions !" );
590 p_sessions
[i_session_id
- 1].i_slot
= i_slot
;
591 p_sessions
[i_session_id
- 1].i_resource_id
= i_resource_id
;
592 p_sessions
[i_session_id
- 1].pf_close
= NULL
;
593 p_sessions
[i_session_id
- 1].pf_manage
= NULL
;
595 if ( i_resource_id
== RI_RESOURCE_MANAGER
596 || i_resource_id
== RI_APPLICATION_INFORMATION
597 || i_resource_id
== RI_CONDITIONAL_ACCESS_SUPPORT
598 || i_resource_id
== RI_DATE_TIME
599 || i_resource_id
== RI_MMI
)
604 p_response
[0] = ST_OPEN_SESSION_RESPONSE
;
606 p_response
[2] = i_status
;
607 p_response
[3] = p_spdu
[2];
608 p_response
[4] = p_spdu
[3];
609 p_response
[5] = p_spdu
[4];
610 p_response
[6] = p_spdu
[5];
611 p_response
[7] = i_session_id
>> 8;
612 p_response
[8] = i_session_id
& 0xff;
614 if ( TPDUSend( p_access
, i_slot
, T_DATA_LAST
, p_response
, 9 ) != 0 )
617 "SessionOpen: couldn't send TPDU on slot %d", i_slot
);
621 if ( p_slot
->i_pending_session_id
!= 0 )
622 msg_Warn( p_access
, "overwriting pending session %d",
623 p_slot
->i_pending_session_id
);
624 p_slot
->i_pending_session_id
= i_session_id
;
628 /* unused code for the moment - commented out to keep gcc happy */
629 /*****************************************************************************
631 *****************************************************************************/
632 static void SessionCreate( access_t
* p_access
, int i_slot
, int i_resource_id
)
634 uint8_t p_response
[16];
638 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
640 if ( !p_sessions
[i_session_id
- 1].i_resource_id
)
643 if ( i_session_id
> MAX_SESSIONS
)
645 msg_Err( p_access
, "too many sessions !" );
648 p_sessions
[i_session_id
- 1].i_slot
= i_slot
;
649 p_sessions
[i_session_id
- 1].i_resource_id
= i_resource_id
;
650 p_sessions
[i_session_id
- 1].pf_close
= NULL
;
651 p_sessions
[i_session_id
- 1].pf_manage
= NULL
;
652 p_sessions
[i_session_id
- 1].p_sys
= NULL
;
654 p_response
[0] = ST_CREATE_SESSION
;
656 p_response
[2] = i_resource_id
>> 24;
657 p_response
[3] = (i_resource_id
>> 16) & 0xff;
658 p_response
[4] = (i_resource_id
>> 8) & 0xff;
659 p_response
[5] = i_resource_id
& 0xff;
660 p_response
[6] = i_session_id
>> 8;
661 p_response
[7] = i_session_id
& 0xff;
663 if ( TPDUSend( p_access
, i_slot
, T_DATA_LAST
, p_response
, 4 ) !=
667 "SessionCreate: couldn't send TPDU on slot %d", i_slot
);
673 /*****************************************************************************
674 * SessionCreateResponse
675 *****************************************************************************/
676 static void SessionCreateResponse( access_t
* p_access
, uint8_t i_slot
,
677 uint8_t *p_spdu
, int i_size
)
679 int i_status
= p_spdu
[2];
680 int i_resource_id
= ResourceIdToInt( &p_spdu
[3] );
681 int i_session_id
= ((int)p_spdu
[7] << 8) | p_spdu
[8];
683 if ( i_status
!= SS_OK
)
685 msg_Err( p_access
, "SessionCreateResponse: failed to open session %d"
686 " resource=0x%x status=0x%x", i_session_id
, i_resource_id
,
688 p_sessions
[i_session_id
- 1].i_resource_id
= 0;
692 switch ( i_resource_id
)
694 case RI_RESOURCE_MANAGER
:
695 ResourceManagerOpen( p_access
, i_session_id
); break;
696 case RI_APPLICATION_INFORMATION
:
697 ApplicationInformationOpen( p_access
, i_session_id
); break;
698 case RI_CONDITIONAL_ACCESS_SUPPORT
:
699 ConditionalAccessOpen( p_access
, i_session_id
); break;
701 DateTimeOpen( p_access
, i_session_id
); break;
703 MMIOpen( p_access
, i_session_id
); break;
705 case RI_HOST_CONTROL
:
707 msg_Err( p_access
, "unknown resource id (0x%x)", i_resource_id
);
708 p_sessions
[i_session_id
- 1].i_resource_id
= 0;
712 /*****************************************************************************
714 *****************************************************************************/
715 static void SessionSendClose( access_t
* p_access
, int i_session_id
)
717 uint8_t p_response
[16];
718 uint8_t i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
720 p_response
[0] = ST_CLOSE_SESSION_REQUEST
;
722 p_response
[2] = i_session_id
>> 8;
723 p_response
[3] = i_session_id
& 0xff;
725 if ( TPDUSend( p_access
, i_slot
, T_DATA_LAST
, p_response
, 4 ) !=
729 "SessionSendClose: couldn't send TPDU on slot %d", i_slot
);
734 /*****************************************************************************
736 *****************************************************************************/
737 static void SessionClose( access_t
* p_access
, int i_session_id
)
739 uint8_t p_response
[16];
740 uint8_t i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
742 if ( p_sessions
[i_session_id
- 1].pf_close
!= NULL
)
743 p_sessions
[i_session_id
- 1].pf_close( p_access
, i_session_id
);
744 p_sessions
[i_session_id
- 1].i_resource_id
= 0;
746 p_response
[0] = ST_CLOSE_SESSION_RESPONSE
;
748 p_response
[2] = SS_OK
;
749 p_response
[3] = i_session_id
>> 8;
750 p_response
[4] = i_session_id
& 0xff;
752 if ( TPDUSend( p_access
, i_slot
, T_DATA_LAST
, p_response
, 5 ) !=
756 "SessionClose: couldn't send TPDU on slot %d", i_slot
);
761 /*****************************************************************************
763 *****************************************************************************/
764 static void SPDUHandle( access_t
* p_access
, uint8_t i_slot
,
765 uint8_t *p_spdu
, int i_size
)
771 case ST_SESSION_NUMBER
:
774 i_session_id
= (p_spdu
[2] << 8) | p_spdu
[3];
775 if ( i_session_id
<= MAX_SESSIONS
776 && p_sessions
[i_session_id
- 1].pf_handle
!= NULL
)
777 p_sessions
[i_session_id
- 1].pf_handle( p_access
, i_session_id
,
778 p_spdu
+ 4, i_size
- 4 );
781 case ST_OPEN_SESSION_REQUEST
:
782 if ( i_size
!= 6 || p_spdu
[1] != 0x4 )
784 SessionOpen( p_access
, i_slot
, p_spdu
, i_size
);
787 case ST_CREATE_SESSION_RESPONSE
:
788 if ( i_size
!= 9 || p_spdu
[1] != 0x7 )
790 SessionCreateResponse( p_access
, i_slot
, p_spdu
, i_size
);
793 case ST_CLOSE_SESSION_REQUEST
:
794 if ( i_size
!= 4 || p_spdu
[1] != 0x2 )
796 i_session_id
= ((int)p_spdu
[2] << 8) | p_spdu
[3];
797 SessionClose( p_access
, i_session_id
);
800 case ST_CLOSE_SESSION_RESPONSE
:
801 if ( i_size
!= 5 || p_spdu
[1] != 0x3 )
803 i_session_id
= ((int)p_spdu
[3] << 8) | p_spdu
[4];
806 msg_Err( p_access
, "closing a session which is not allocated (%d)",
811 if ( p_sessions
[i_session_id
- 1].pf_close
!= NULL
)
812 p_sessions
[i_session_id
- 1].pf_close( p_access
,
814 p_sessions
[i_session_id
- 1].i_resource_id
= 0;
819 msg_Err( p_access
, "unexpected tag in SPDUHandle (%x)", p_spdu
[0] );
829 #define AOT_NONE 0x000000
830 #define AOT_PROFILE_ENQ 0x9F8010
831 #define AOT_PROFILE 0x9F8011
832 #define AOT_PROFILE_CHANGE 0x9F8012
833 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
834 #define AOT_APPLICATION_INFO 0x9F8021
835 #define AOT_ENTER_MENU 0x9F8022
836 #define AOT_CA_INFO_ENQ 0x9F8030
837 #define AOT_CA_INFO 0x9F8031
838 #define AOT_CA_PMT 0x9F8032
839 #define AOT_CA_PMT_REPLY 0x9F8033
840 #define AOT_CA_UPDATE 0x9F8034
841 #define AOT_TUNE 0x9F8400
842 #define AOT_REPLACE 0x9F8401
843 #define AOT_CLEAR_REPLACE 0x9F8402
844 #define AOT_ASK_RELEASE 0x9F8403
845 #define AOT_DATE_TIME_ENQ 0x9F8440
846 #define AOT_DATE_TIME 0x9F8441
847 #define AOT_CLOSE_MMI 0x9F8800
848 #define AOT_DISPLAY_CONTROL 0x9F8801
849 #define AOT_DISPLAY_REPLY 0x9F8802
850 #define AOT_TEXT_LAST 0x9F8803
851 #define AOT_TEXT_MORE 0x9F8804
852 #define AOT_KEYPAD_CONTROL 0x9F8805
853 #define AOT_KEYPRESS 0x9F8806
854 #define AOT_ENQ 0x9F8807
855 #define AOT_ANSW 0x9F8808
856 #define AOT_MENU_LAST 0x9F8809
857 #define AOT_MENU_MORE 0x9F880A
858 #define AOT_MENU_ANSW 0x9F880B
859 #define AOT_LIST_LAST 0x9F880C
860 #define AOT_LIST_MORE 0x9F880D
861 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
862 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
863 #define AOT_DISPLAY_MESSAGE 0x9F8810
864 #define AOT_SCENE_END_MARK 0x9F8811
865 #define AOT_SCENE_DONE 0x9F8812
866 #define AOT_SCENE_CONTROL 0x9F8813
867 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
868 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
869 #define AOT_FLUSH_DOWNLOAD 0x9F8816
870 #define AOT_DOWNLOAD_REPLY 0x9F8817
871 #define AOT_COMMS_CMD 0x9F8C00
872 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
873 #define AOT_COMMS_REPLY 0x9F8C02
874 #define AOT_COMMS_SEND_LAST 0x9F8C03
875 #define AOT_COMMS_SEND_MORE 0x9F8C04
876 #define AOT_COMMS_RCV_LAST 0x9F8C05
877 #define AOT_COMMS_RCV_MORE 0x9F8C06
879 /*****************************************************************************
881 *****************************************************************************/
882 static int APDUGetTag( const uint8_t *p_apdu
, int i_size
)
887 for ( i
= 0; i
< 3; i
++ )
888 t
= (t
<< 8) | *p_apdu
++;
895 /*****************************************************************************
897 *****************************************************************************/
898 static uint8_t *APDUGetLength( uint8_t *p_apdu
, int *pi_size
)
900 return GetLength( &p_apdu
[3], pi_size
);
903 /*****************************************************************************
905 *****************************************************************************/
906 static int APDUSend( access_t
* p_access
, int i_session_id
, int i_tag
,
907 uint8_t *p_data
, int i_size
)
909 uint8_t *p_apdu
= malloc( i_size
+ 12 );
914 *p
++ = (i_tag
>> 16);
915 *p
++ = (i_tag
>> 8) & 0xff;
917 p
= SetLength( p
, i_size
);
919 memcpy( p
, p_data
, i_size
);
920 if ( i_ca_type
== CA_CI_LINK
)
922 i_ret
= SPDUSend( p_access
, i_session_id
, p_apdu
, i_size
+ p
- p_apdu
);
926 if ( i_size
+ p
- p_apdu
> 256 )
928 msg_Err( p_access
, "CAM: apdu overflow" );
933 ca_msg
.length
= i_size
+ p
- p_apdu
;
934 if ( i_size
== 0 ) ca_msg
.length
=3;
935 memcpy( ca_msg
.msg
, p_apdu
, i_size
+ p
- p_apdu
);
936 i_ret
= ioctl(i_ca_handle
, CA_SEND_MSG
, &ca_msg
);
939 msg_Err( p_access
, "Error sending to CAM: %m" );
952 /*****************************************************************************
953 * ResourceManagerHandle
954 *****************************************************************************/
955 static void ResourceManagerHandle( access_t
* p_access
, int i_session_id
,
956 uint8_t *p_apdu
, int i_size
)
958 int i_tag
= APDUGetTag( p_apdu
, i_size
);
962 case AOT_PROFILE_ENQ
:
964 int resources
[] = { htonl(RI_RESOURCE_MANAGER
),
965 htonl(RI_APPLICATION_INFORMATION
),
966 htonl(RI_CONDITIONAL_ACCESS_SUPPORT
),
970 APDUSend( p_access
, i_session_id
, AOT_PROFILE
, (uint8_t*)resources
,
975 APDUSend( p_access
, i_session_id
, AOT_PROFILE_CHANGE
, NULL
, 0 );
979 msg_Err( p_access
, "unexpected tag in ResourceManagerHandle (0x%x)",
984 /*****************************************************************************
985 * ResourceManagerOpen
986 *****************************************************************************/
987 static void ResourceManagerOpen( access_t
* p_access
, int i_session_id
)
990 msg_Dbg( p_access
, "opening ResourceManager session (%d)", i_session_id
);
992 p_sessions
[i_session_id
- 1].pf_handle
= ResourceManagerHandle
;
994 APDUSend( p_access
, i_session_id
, AOT_PROFILE_ENQ
, NULL
, 0 );
998 * Application Information
1001 /*****************************************************************************
1002 * ApplicationInformationEnterMenu
1003 *****************************************************************************/
1004 static void ApplicationInformationEnterMenu( access_t
* p_access
,
1007 int i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
1009 msg_Dbg( p_access
, "entering MMI menus on session %d", i_session_id
);
1010 APDUSend( p_access
, i_session_id
, AOT_ENTER_MENU
, NULL
, 0 );
1011 p_slots
[i_slot
].b_mmi_expected
= true;
1014 /*****************************************************************************
1015 * ApplicationInformationHandle
1016 *****************************************************************************/
1017 static void ApplicationInformationHandle( access_t
* p_access
, int i_session_id
,
1018 uint8_t *p_apdu
, int i_size
)
1020 int i_tag
= APDUGetTag( p_apdu
, i_size
);
1024 case AOT_APPLICATION_INFO
:
1026 int i_type
, i_manufacturer
, i_code
;
1028 uint8_t *d
= APDUGetLength( p_apdu
, &l
);
1033 i_manufacturer
= ((int)d
[0] << 8) | d
[1];
1035 i_code
= ((int)d
[0] << 8) | d
[1];
1037 d
= GetLength( d
, &l
);
1040 char *psz_name
= malloc(l
+ 1);
1041 memcpy( psz_name
, d
, l
);
1043 msg_Info( p_access
, "CAM: %s, %02X, %04X, %04X",
1044 psz_name
, i_type
, i_manufacturer
, i_code
);
1045 switch (i_print_type
)
1048 psz_name
= dvb_string_xml_escape(psz_name
);
1049 fprintf(print_fh
, "<STATUS type=\"cam\" status=\"1\" cam_name=\"%s\" cam_type=\"%d\" cam_manufacturer=\"%d\" cam_product=\"%d\" />\n",
1050 psz_name
, i_type
, i_manufacturer
, i_code
);
1053 fprintf(print_fh
, "CAM name: %s type: %d manufacturer: %d product: %d\n",
1054 psz_name
, i_type
, i_manufacturer
, i_code
);
1065 "unexpected tag in ApplicationInformationHandle (0x%x)",
1070 /*****************************************************************************
1071 * ApplicationInformationOpen
1072 *****************************************************************************/
1073 static void ApplicationInformationOpen( access_t
* p_access
, int i_session_id
)
1076 msg_Dbg( p_access
, "opening ApplicationInformation session (%d)", i_session_id
);
1078 p_sessions
[i_session_id
- 1].pf_handle
= ApplicationInformationHandle
;
1080 APDUSend( p_access
, i_session_id
, AOT_APPLICATION_INFO_ENQ
, NULL
, 0 );
1084 * Conditional Access
1089 int i_nb_system_ids
;
1090 uint16_t *pi_system_ids
;
1092 int i_selected_programs
;
1096 static bool CheckSystemID( system_ids_t
*p_ids
, uint16_t i_id
)
1099 if( p_ids
== NULL
) return false;
1100 if( p_ids
->b_high_level
) return true;
1102 for ( i
= 0; i
< p_ids
->i_nb_system_ids
; i
++ )
1103 if ( p_ids
->pi_system_ids
[i
] == i_id
)
1109 /*****************************************************************************
1111 *****************************************************************************/
1112 static bool HasCADescriptors( system_ids_t
*p_ids
, uint8_t *p_descs
)
1114 const uint8_t *p_desc
;
1117 while ( (p_desc
= descs_get_desc( p_descs
, j
)) != NULL
)
1119 uint8_t i_tag
= desc_get_tag( p_desc
);
1122 if ( i_tag
== 0x9 && desc09_validate( p_desc
)
1123 && CheckSystemID( p_ids
, desc09_get_sysid( p_desc
) ) )
1130 static void CopyCADescriptors( system_ids_t
*p_ids
, uint8_t i_cmd
,
1131 uint8_t *p_infos
, uint8_t *p_descs
)
1133 const uint8_t *p_desc
;
1134 uint16_t j
= 0, k
= 0;
1136 capmti_init( p_infos
);
1137 capmti_set_length( p_infos
, 0xfff );
1138 capmti_set_cmd( p_infos
, i_cmd
);
1140 while ( (p_desc
= descs_get_desc( p_descs
, j
)) != NULL
)
1142 uint8_t i_tag
= desc_get_tag( p_desc
);
1145 if ( i_tag
== 0x9 && desc09_validate( p_desc
)
1146 && CheckSystemID( p_ids
, desc09_get_sysid( p_desc
) ) )
1148 uint8_t *p_info
= capmti_get_info( p_infos
, k
);
1150 memcpy( p_info
, p_desc
,
1151 DESC_HEADER_SIZE
+ desc_get_length( p_desc
) );
1157 uint8_t *p_info
= capmti_get_info( p_infos
, k
);
1158 capmti_set_length( p_infos
, p_info
- p_infos
- DESCS_HEADER_SIZE
);
1161 capmti_set_length( p_infos
, 0 );
1164 static uint8_t *CAPMTBuild( access_t
* p_access
, int i_session_id
,
1165 uint8_t *p_pmt
, uint8_t i_list_mgt
,
1166 uint8_t i_cmd
, int *pi_capmt_size
)
1168 system_ids_t
*p_ids
=
1169 (system_ids_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1171 uint8_t *p_capmt
, *p_capmt_n
;
1173 bool b_has_ca
= HasCADescriptors( p_ids
, pmt_get_descs( p_pmt
) );
1174 bool b_has_es
= false;
1177 while ( (p_es
= pmt_get_es( p_pmt
, j
)) != NULL
)
1179 uint16_t i_pid
= pmtn_get_pid( p_es
);
1182 if ( demux_PIDIsSelected( i_pid
) )
1186 || HasCADescriptors( p_ids
, pmtn_get_descs( p_es
) );
1199 "no compatible scrambling system for SID %d on session %d",
1200 pmt_get_program( p_pmt
), i_session_id
);
1205 p_capmt
= capmt_allocate();
1206 capmt_init( p_capmt
);
1207 capmt_set_listmanagement( p_capmt
, i_list_mgt
);
1208 capmt_set_program( p_capmt
, pmt_get_program( p_pmt
) );
1209 capmt_set_version( p_capmt
, psi_get_version( p_pmt
) );
1211 CopyCADescriptors( p_ids
, i_cmd
, capmt_get_infos( p_capmt
),
1212 pmt_get_descs( p_pmt
) );
1215 while ( (p_es
= pmt_get_es( p_pmt
, j
)) != NULL
)
1217 uint16_t i_pid
= pmtn_get_pid( p_es
);
1220 if ( !demux_PIDIsSelected( i_pid
) )
1223 p_capmt_n
= capmt_get_es( p_capmt
, k
);
1226 capmtn_init( p_capmt_n
);
1227 capmtn_set_streamtype( p_capmt_n
, pmtn_get_streamtype( p_es
) );
1228 capmtn_set_pid( p_capmt_n
, pmtn_get_pid( p_es
) );
1230 CopyCADescriptors( p_ids
, i_cmd
, capmtn_get_infos( p_capmt_n
),
1231 pmtn_get_descs( p_es
) );
1234 p_capmt_n
= capmt_get_es( p_capmt
, k
);
1235 *pi_capmt_size
= p_capmt_n
- p_capmt
;
1240 /*****************************************************************************
1242 *****************************************************************************/
1243 static void CAPMTFirst( access_t
* p_access
, int i_session_id
, uint8_t *p_pmt
)
1248 msg_Dbg( p_access
, "adding first CAPMT for SID %d on session %d",
1249 pmt_get_program( p_pmt
), i_session_id
);
1251 p_capmt
= CAPMTBuild( p_access
, i_session_id
, p_pmt
,
1252 0x3 /* only */, 0x1 /* ok_descrambling */,
1257 APDUSend( p_access
, i_session_id
, AOT_CA_PMT
, p_capmt
, i_capmt_size
);
1262 /*****************************************************************************
1264 *****************************************************************************/
1265 static void CAPMTAdd( access_t
* p_access
, int i_session_id
, uint8_t *p_pmt
)
1267 system_ids_t
*p_ids
=
1268 (system_ids_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1272 p_ids
->i_selected_programs
++;
1273 if( p_ids
->i_selected_programs
== 1 )
1275 CAPMTFirst( p_access
, i_session_id
, p_pmt
);
1279 msg_Dbg( p_access
, "adding CAPMT for SID %d on session %d",
1280 pmt_get_program( p_pmt
), i_session_id
);
1282 p_capmt
= CAPMTBuild( p_access
, i_session_id
, p_pmt
,
1283 0x4 /* add */, 0x1 /* ok_descrambling */,
1288 APDUSend( p_access
, i_session_id
, AOT_CA_PMT
, p_capmt
, i_capmt_size
);
1293 /*****************************************************************************
1295 *****************************************************************************/
1296 static void CAPMTUpdate( access_t
* p_access
, int i_session_id
, uint8_t *p_pmt
)
1301 msg_Dbg( p_access
, "updating CAPMT for SID %d on session %d",
1302 pmt_get_program( p_pmt
), i_session_id
);
1304 p_capmt
= CAPMTBuild( p_access
, i_session_id
, p_pmt
,
1305 0x5 /* update */, 0x1 /* ok_descrambling */,
1310 APDUSend( p_access
, i_session_id
, AOT_CA_PMT
, p_capmt
, i_capmt_size
);
1315 /*****************************************************************************
1317 *****************************************************************************/
1318 static void CAPMTDelete( access_t
* p_access
, int i_session_id
, uint8_t *p_pmt
)
1320 system_ids_t
*p_ids
=
1321 (system_ids_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1325 p_ids
->i_selected_programs
--;
1326 msg_Dbg( p_access
, "deleting CAPMT for SID %d on session %d",
1327 pmt_get_program( p_pmt
), i_session_id
);
1329 p_capmt
= CAPMTBuild( p_access
, i_session_id
, p_pmt
,
1330 0x5 /* update */, 0x4 /* not selected */,
1335 APDUSend( p_access
, i_session_id
, AOT_CA_PMT
, p_capmt
, i_capmt_size
);
1340 /*****************************************************************************
1341 * ConditionalAccessHandle
1342 *****************************************************************************/
1343 static void ConditionalAccessHandle( access_t
* p_access
, int i_session_id
,
1344 uint8_t *p_apdu
, int i_size
)
1346 system_ids_t
*p_ids
=
1347 (system_ids_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1348 int i_tag
= APDUGetTag( p_apdu
, i_size
);
1356 uint8_t *d
= APDUGetLength( p_apdu
, &l
);
1357 msg_Dbg( p_access
, "CA system IDs supported by the application :" );
1359 if ( p_ids
->i_nb_system_ids
)
1360 free( p_ids
->pi_system_ids
);
1361 p_ids
->i_nb_system_ids
= l
/ 2;
1362 p_ids
->pi_system_ids
= malloc( p_ids
->i_nb_system_ids
1363 * sizeof(uint16_t) );
1365 for ( i
= 0; i
< p_ids
->i_nb_system_ids
; i
++ )
1367 p_ids
->pi_system_ids
[i
] = ((uint16_t)d
[0] << 8) | d
[1];
1369 msg_Dbg( p_access
, "- 0x%x", p_ids
->pi_system_ids
[i
] );
1372 demux_ResendCAPMTs();
1377 /* http://www.cablelabs.com/specifications/OC-SP-HOSTPOD-IF-I08-011221.pdf */
1378 case AOT_CA_PMT_REPLY
:
1379 /* We do not care */
1384 "unexpected tag in ConditionalAccessHandle (0x%x)",
1389 /*****************************************************************************
1390 * ConditionalAccessClose
1391 *****************************************************************************/
1392 static void ConditionalAccessClose( access_t
* p_access
, int i_session_id
)
1394 system_ids_t
*p_ids
=
1395 (system_ids_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1397 msg_Dbg( p_access
, "closing ConditionalAccess session (%d)", i_session_id
);
1399 if ( p_ids
->i_nb_system_ids
)
1400 free( p_ids
->pi_system_ids
);
1402 free( p_sessions
[i_session_id
- 1].p_sys
);
1405 /*****************************************************************************
1406 * ConditionalAccessOpen
1407 *****************************************************************************/
1408 static void ConditionalAccessOpen( access_t
* p_access
, int i_session_id
)
1411 msg_Dbg( p_access
, "opening ConditionalAccess session (%d)", i_session_id
);
1413 p_sessions
[i_session_id
- 1].pf_handle
= ConditionalAccessHandle
;
1414 p_sessions
[i_session_id
- 1].pf_close
= ConditionalAccessClose
;
1415 p_sessions
[i_session_id
- 1].p_sys
= malloc(sizeof(system_ids_t
));
1416 memset( p_sessions
[i_session_id
- 1].p_sys
, 0,
1417 sizeof(system_ids_t
) );
1419 APDUSend( p_access
, i_session_id
, AOT_CA_INFO_ENQ
, NULL
, 0 );
1430 struct ev_timer watcher
;
1433 /*****************************************************************************
1435 *****************************************************************************/
1436 static void DateTimeSend( access_t
* p_access
, int i_session_id
)
1438 date_time_t
*p_date
=
1439 (date_time_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1441 time_t t
= time(NULL
);
1445 if ( gmtime_r(&t
, &tm_gmt
) && localtime_r(&t
, &tm_loc
) )
1447 int Y
= tm_gmt
.tm_year
;
1448 int M
= tm_gmt
.tm_mon
+ 1;
1449 int D
= tm_gmt
.tm_mday
;
1450 int L
= (M
== 1 || M
== 2) ? 1 : 0;
1451 int MJD
= 14956 + D
+ (int)((Y
- L
) * 365.25)
1452 + (int)((M
+ 1 + L
* 12) * 30.6001);
1453 uint8_t p_response
[7];
1455 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
1457 p_response
[0] = htons(MJD
) >> 8;
1458 p_response
[1] = htons(MJD
) & 0xff;
1459 p_response
[2] = DEC2BCD(tm_gmt
.tm_hour
);
1460 p_response
[3] = DEC2BCD(tm_gmt
.tm_min
);
1461 p_response
[4] = DEC2BCD(tm_gmt
.tm_sec
);
1462 p_response
[5] = htons(tm_loc
.tm_gmtoff
/ 60) >> 8;
1463 p_response
[6] = htons(tm_loc
.tm_gmtoff
/ 60) & 0xff;
1465 APDUSend( p_access
, i_session_id
, AOT_DATE_TIME
, p_response
, 7 );
1467 ev_timer_again(event_loop
, &p_date
->watcher
);
1471 static void _DateTimeSend(struct ev_loop
*loop
, struct ev_timer
*w
, int revents
)
1473 date_time_t
*p_date
= container_of(w
, date_time_t
, watcher
);
1474 DateTimeSend( NULL
, p_date
->i_session_id
);
1477 /*****************************************************************************
1479 *****************************************************************************/
1480 static void DateTimeHandle( access_t
* p_access
, int i_session_id
,
1481 uint8_t *p_apdu
, int i_size
)
1483 date_time_t
*p_date
=
1484 (date_time_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1486 int i_tag
= APDUGetTag( p_apdu
, i_size
);
1490 case AOT_DATE_TIME_ENQ
:
1493 const uint8_t *d
= APDUGetLength( p_apdu
, &l
);
1497 p_date
->i_interval
= *d
;
1498 msg_Dbg( p_access
, "DateTimeHandle : interval set to %d",
1499 p_date
->i_interval
);
1502 p_date
->i_interval
= 0;
1504 ev_timer_stop(event_loop
, &p_date
->watcher
);
1505 ev_timer_set(&p_date
->watcher
, p_date
->i_interval
,
1506 p_date
->i_interval
);
1507 DateTimeSend( p_access
, i_session_id
);
1511 msg_Err( p_access
, "unexpected tag in DateTimeHandle (0x%x)", i_tag
);
1515 /*****************************************************************************
1517 *****************************************************************************/
1518 static void DateTimeClose( access_t
* p_access
, int i_session_id
)
1520 date_time_t
*p_date
=
1521 (date_time_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1522 ev_timer_stop(event_loop
, &p_date
->watcher
);
1524 msg_Dbg( p_access
, "closing DateTime session (%d)", i_session_id
);
1529 /*****************************************************************************
1531 *****************************************************************************/
1532 static void DateTimeOpen( access_t
* p_access
, int i_session_id
)
1534 msg_Dbg( p_access
, "opening DateTime session (%d)", i_session_id
);
1536 p_sessions
[i_session_id
- 1].pf_handle
= DateTimeHandle
;
1537 p_sessions
[i_session_id
- 1].pf_manage
= NULL
;
1538 p_sessions
[i_session_id
- 1].pf_close
= DateTimeClose
;
1539 p_sessions
[i_session_id
- 1].p_sys
= malloc(sizeof(date_time_t
));
1540 memset( p_sessions
[i_session_id
- 1].p_sys
, 0, sizeof(date_time_t
) );
1542 date_time_t
*p_date
=
1543 (date_time_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1544 p_date
->i_session_id
= i_session_id
;
1545 ev_timer_init(&p_date
->watcher
, _DateTimeSend
, 0, 0);
1547 DateTimeSend( p_access
, i_session_id
);
1554 /* Display Control Commands */
1556 #define DCC_SET_MMI_MODE 0x01
1557 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
1558 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
1559 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
1560 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
1564 #define MM_HIGH_LEVEL 0x01
1565 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
1566 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
1568 /* Display Reply IDs */
1570 #define DRI_MMI_MODE_ACK 0x01
1571 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
1572 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
1573 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
1574 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
1575 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
1576 #define DRI_UNKNOWN_MMI_MODE 0xF1
1577 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
1581 #define EF_BLIND 0x01
1585 #define AI_CANCEL 0x00
1586 #define AI_ANSWER 0x01
1590 en50221_mmi_object_t last_object
;
1593 static inline void en50221_MMIFree( en50221_mmi_object_t
*p_object
)
1597 switch ( p_object
->i_object_type
)
1599 case EN50221_MMI_ENQ
:
1600 free( p_object
->u
.enq
.psz_text
);
1603 case EN50221_MMI_ANSW
:
1604 if ( p_object
->u
.answ
.b_ok
)
1606 free( p_object
->u
.answ
.psz_answ
);
1610 case EN50221_MMI_MENU
:
1611 case EN50221_MMI_LIST
:
1612 free( p_object
->u
.menu
.psz_title
);
1613 free( p_object
->u
.menu
.psz_subtitle
);
1614 free( p_object
->u
.menu
.psz_bottom
);
1615 for ( i
= 0; i
< p_object
->u
.menu
.i_choices
; i
++ )
1617 free( p_object
->u
.menu
.ppsz_choices
[i
] );
1619 free( p_object
->u
.menu
.ppsz_choices
);
1627 /*****************************************************************************
1629 *****************************************************************************/
1630 static void MMISendObject( access_t
*p_access
, int i_session_id
,
1631 en50221_mmi_object_t
*p_object
)
1633 int i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
1637 switch ( p_object
->i_object_type
)
1639 case EN50221_MMI_ANSW
:
1641 i_size
= 1 + strlen( p_object
->u
.answ
.psz_answ
);
1642 p_data
= malloc( i_size
);
1643 p_data
[0] = (p_object
->u
.answ
.b_ok
== true) ? 0x1 : 0x0;
1644 strncpy( (char *)&p_data
[1], p_object
->u
.answ
.psz_answ
, i_size
- 1 );
1647 case EN50221_MMI_MENU_ANSW
:
1648 i_tag
= AOT_MENU_ANSW
;
1650 p_data
= malloc( i_size
);
1651 p_data
[0] = p_object
->u
.menu_answ
.i_choice
;
1655 msg_Err( p_access
, "unknown MMI object %d", p_object
->i_object_type
);
1659 APDUSend( p_access
, i_session_id
, i_tag
, p_data
, i_size
);
1662 p_slots
[i_slot
].b_mmi_expected
= true;
1665 /*****************************************************************************
1667 *****************************************************************************/
1668 static void MMISendClose( access_t
*p_access
, int i_session_id
)
1670 int i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
1672 APDUSend( p_access
, i_session_id
, AOT_CLOSE_MMI
, NULL
, 0 );
1674 p_slots
[i_slot
].b_mmi_expected
= true;
1677 /*****************************************************************************
1679 *****************************************************************************/
1680 static void MMIDisplayReply( access_t
*p_access
, int i_session_id
)
1682 uint8_t p_response
[2];
1684 p_response
[0] = DRI_MMI_MODE_ACK
;
1685 p_response
[1] = MM_HIGH_LEVEL
;
1687 APDUSend( p_access
, i_session_id
, AOT_DISPLAY_REPLY
, p_response
, 2 );
1689 msg_Dbg( p_access
, "sending DisplayReply on session (%d)", i_session_id
);
1692 /*****************************************************************************
1694 *****************************************************************************/
1695 static char *MMIGetText( access_t
*p_access
, uint8_t **pp_apdu
, int *pi_size
)
1697 int i_tag
= APDUGetTag( *pp_apdu
, *pi_size
);
1701 if ( i_tag
!= AOT_TEXT_LAST
)
1703 msg_Err( p_access
, "unexpected text tag: %06x", i_tag
);
1705 return strdup( "" );
1708 d
= APDUGetLength( *pp_apdu
, &l
);
1713 return dvb_string_get( d
, l
, demux_Iconv
, p_access
);
1716 /*****************************************************************************
1718 *****************************************************************************/
1719 static void MMIHandleEnq( access_t
*p_access
, int i_session_id
,
1720 uint8_t *p_apdu
, int i_size
)
1722 mmi_t
*p_mmi
= (mmi_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1723 int i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
1725 uint8_t *d
= APDUGetLength( p_apdu
, &l
);
1727 en50221_MMIFree( &p_mmi
->last_object
);
1728 p_mmi
->last_object
.i_object_type
= EN50221_MMI_ENQ
;
1729 p_mmi
->last_object
.u
.enq
.b_blind
= (*d
& 0x1) ? true : false;
1730 d
+= 2; /* skip answer_text_length because it is not mandatory */
1732 p_mmi
->last_object
.u
.enq
.psz_text
= malloc( l
+ 1 );
1733 strncpy( p_mmi
->last_object
.u
.enq
.psz_text
, (char *)d
, l
);
1734 p_mmi
->last_object
.u
.enq
.psz_text
[l
] = '\0';
1736 msg_Dbg( p_access
, "MMI enq: %s%s", p_mmi
->last_object
.u
.enq
.psz_text
,
1737 p_mmi
->last_object
.u
.enq
.b_blind
== true ? " (blind)" : "" );
1739 p_slots
[i_slot
].b_mmi_expected
= false;
1740 p_slots
[i_slot
].b_mmi_undisplayed
= true;
1743 /*****************************************************************************
1745 *****************************************************************************/
1746 static void MMIHandleMenu( access_t
*p_access
, int i_session_id
, int i_tag
,
1747 uint8_t *p_apdu
, int i_size
)
1749 mmi_t
*p_mmi
= (mmi_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1750 int i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
1752 uint8_t *d
= APDUGetLength( p_apdu
, &l
);
1754 en50221_MMIFree( &p_mmi
->last_object
);
1755 p_mmi
->last_object
.i_object_type
= (i_tag
== AOT_MENU_LAST
) ?
1756 EN50221_MMI_MENU
: EN50221_MMI_LIST
;
1757 p_mmi
->last_object
.u
.menu
.i_choices
= 0;
1758 p_mmi
->last_object
.u
.menu
.ppsz_choices
= NULL
;
1762 l
--; d
++; /* choice_nb */
1764 #define GET_FIELD( x ) \
1767 p_mmi->last_object.u.menu.psz_##x \
1768 = MMIGetText( p_access, &d, &l ); \
1769 msg_Dbg( p_access, "MMI " STRINGIFY( x ) ": %s", \
1770 p_mmi->last_object.u.menu.psz_##x ); \
1774 GET_FIELD( subtitle
);
1775 GET_FIELD( bottom
);
1780 char *psz_text
= MMIGetText( p_access
, &d
, &l
);
1781 TAB_APPEND( p_mmi
->last_object
.u
.menu
.i_choices
,
1782 p_mmi
->last_object
.u
.menu
.ppsz_choices
,
1784 msg_Dbg( p_access
, "MMI choice: %s", psz_text
);
1788 p_slots
[i_slot
].b_mmi_expected
= false;
1789 p_slots
[i_slot
].b_mmi_undisplayed
= true;
1792 /*****************************************************************************
1794 *****************************************************************************/
1795 static void MMIHandle( access_t
*p_access
, int i_session_id
,
1796 uint8_t *p_apdu
, int i_size
)
1798 int i_tag
= APDUGetTag( p_apdu
, i_size
);
1802 case AOT_DISPLAY_CONTROL
:
1805 uint8_t *d
= APDUGetLength( p_apdu
, &l
);
1811 case DCC_SET_MMI_MODE
:
1812 if ( l
== 2 && d
[1] == MM_HIGH_LEVEL
)
1813 MMIDisplayReply( p_access
, i_session_id
);
1815 msg_Err( p_access
, "unsupported MMI mode %02x", d
[1] );
1819 msg_Err( p_access
, "unsupported display control command %02x",
1828 MMIHandleEnq( p_access
, i_session_id
, p_apdu
, i_size
);
1833 MMIHandleMenu( p_access
, i_session_id
, i_tag
, p_apdu
, i_size
);
1837 SessionSendClose( p_access
, i_session_id
);
1841 msg_Err( p_access
, "unexpected tag in MMIHandle (0x%x)", i_tag
);
1845 /*****************************************************************************
1847 *****************************************************************************/
1848 static void MMIClose( access_t
*p_access
, int i_session_id
)
1850 int i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
1851 mmi_t
*p_mmi
= (mmi_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1853 en50221_MMIFree( &p_mmi
->last_object
);
1854 free( p_sessions
[i_session_id
- 1].p_sys
);
1856 msg_Dbg( p_access
, "closing MMI session (%d)", i_session_id
);
1858 p_slots
[i_slot
].b_mmi_expected
= false;
1859 p_slots
[i_slot
].b_mmi_undisplayed
= true;
1862 /*****************************************************************************
1864 *****************************************************************************/
1865 static void MMIOpen( access_t
*p_access
, int i_session_id
)
1869 msg_Dbg( p_access
, "opening MMI session (%d)", i_session_id
);
1871 p_sessions
[i_session_id
- 1].pf_handle
= MMIHandle
;
1872 p_sessions
[i_session_id
- 1].pf_close
= MMIClose
;
1873 p_sessions
[i_session_id
- 1].p_sys
= malloc(sizeof(mmi_t
));
1874 p_mmi
= (mmi_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1875 p_mmi
->last_object
.i_object_type
= EN50221_MMI_NONE
;
1883 /*****************************************************************************
1884 * InitSlot: Open the transport layer
1885 *****************************************************************************/
1886 static void InitSlot( access_t
* p_access
, int i_slot
)
1888 if ( TPDUSend( p_access
, i_slot
, T_CREATE_TC
, NULL
, 0 ) != 0 )
1889 msg_Err( p_access
, "en50221_Init: couldn't send TPDU on slot %d",
1893 /*****************************************************************************
1895 *****************************************************************************/
1896 static void ResetSlot( int i_slot
)
1898 ci_slot_t
*p_slot
= &p_slots
[i_slot
];
1901 switch (i_print_type
)
1904 fprintf(print_fh
, "<STATUS type=\"cam\" status=\"0\" />\n");
1907 fprintf(print_fh
, "CAM none\n");
1913 if ( ioctl( i_ca_handle
, CA_RESET
, 1 << i_slot
) != 0 )
1914 msg_Err( NULL
, "en50221_Poll: couldn't reset slot %d", i_slot
);
1915 p_slot
->b_active
= false;
1916 ev_timer_init(&p_slot
->init_watcher
, ResetSlotCb
,
1917 CAM_INIT_TIMEOUT
/ 1000000., 0);
1918 ev_timer_start(event_loop
, &p_slot
->init_watcher
);
1919 p_slot
->b_expect_answer
= false;
1920 p_slot
->b_mmi_expected
= false;
1921 p_slot
->b_mmi_undisplayed
= false;
1922 if ( p_slot
->p_recv
!= NULL
)
1924 free( p_slot
->p_recv
->p_data
);
1925 free( p_slot
->p_recv
);
1927 p_slot
->p_recv
= NULL
;
1928 while ( p_slot
->p_send
!= NULL
)
1930 en50221_msg_t
*p_next
= p_slot
->p_send
->p_next
;
1931 free( p_slot
->p_send
->p_data
);
1932 free( p_slot
->p_send
);
1933 p_slot
->p_send
= p_next
;
1935 p_slot
->pp_send_last
= &p_slot
->p_send
;
1937 /* Close all sessions for this slot. */
1938 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
1940 if ( p_sessions
[i_session_id
- 1].i_resource_id
1941 && p_sessions
[i_session_id
- 1].i_slot
== i_slot
)
1943 if ( p_sessions
[i_session_id
- 1].pf_close
!= NULL
)
1945 p_sessions
[i_session_id
- 1].pf_close( NULL
, i_session_id
);
1947 p_sessions
[i_session_id
- 1].i_resource_id
= 0;
1952 static void ResetSlotCb(struct ev_loop
*loop
, struct ev_timer
*w
, int revents
)
1954 ci_slot_t
*p_slot
= container_of(w
, ci_slot_t
, init_watcher
);
1955 int i_slot
= p_slot
- &p_slots
[0];
1957 if ( p_slot
->b_active
|| !p_slot
->b_expect_answer
)
1960 msg_Warn( NULL
, "no answer from CAM, resetting slot %d",
1962 switch (i_print_type
) {
1965 "<EVENT type=\"reset\" cause=\"cam_mute\" />\n");
1968 fprintf(print_fh
, "reset cause: cam_mute\n");
1974 ResetSlot( i_slot
);
1979 * External entry points
1982 /*****************************************************************************
1983 * en50221_Init : Initialize the CAM for en50221
1984 *****************************************************************************/
1985 void en50221_Init( void )
1990 memset( &caps
, 0, sizeof( ca_caps_t
));
1992 sprintf( psz_tmp
, "/dev/dvb/adapter%d/ca%d", i_adapter
, i_canum
);
1993 if( (i_ca_handle
= open(psz_tmp
, O_RDWR
| O_NONBLOCK
)) < 0 )
1995 msg_Warn( NULL
, "failed opening CAM device %s (%s)",
1996 psz_tmp
, strerror(errno
) );
2001 if ( ioctl( i_ca_handle
, CA_GET_CAP
, &caps
) != 0 )
2003 msg_Err( NULL
, "failed getting CAM capabilities (%s)",
2005 close( i_ca_handle
);
2010 /* Output CA capabilities */
2011 msg_Dbg( NULL
, "CA interface with %d %s", caps
.slot_num
,
2012 caps
.slot_num
== 1 ? "slot" : "slots" );
2013 if ( caps
.slot_type
& CA_CI
)
2014 msg_Dbg( NULL
, " CI high level interface type" );
2015 if ( caps
.slot_type
& CA_CI_LINK
)
2016 msg_Dbg( NULL
, " CI link layer level interface type" );
2017 if ( caps
.slot_type
& CA_CI_PHYS
)
2018 msg_Dbg( NULL
, " CI physical layer level interface type (not supported) " );
2019 if ( caps
.slot_type
& CA_DESCR
)
2020 msg_Dbg( NULL
, " built-in descrambler detected" );
2021 if ( caps
.slot_type
& CA_SC
)
2022 msg_Dbg( NULL
, " simple smart card interface" );
2024 msg_Dbg( NULL
, " %d available %s", caps
.descr_num
,
2025 caps
.descr_num
== 1 ? "descrambler (key)" : "descramblers (keys)" );
2026 if ( caps
.descr_type
& CA_ECD
)
2027 msg_Dbg( NULL
, " ECD scrambling system supported" );
2028 if ( caps
.descr_type
& CA_NDS
)
2029 msg_Dbg( NULL
, " NDS scrambling system supported" );
2030 if ( caps
.descr_type
& CA_DSS
)
2031 msg_Dbg( NULL
, " DSS scrambling system supported" );
2033 if ( caps
.slot_num
== 0 )
2035 msg_Err( NULL
, "CAM module with no slots" );
2036 close( i_ca_handle
);
2041 if( caps
.slot_type
& CA_CI_LINK
)
2042 i_ca_type
= CA_CI_LINK
;
2043 else if( caps
.slot_type
& CA_CI
)
2047 msg_Err( NULL
, "Incompatible CAM interface" );
2048 close( i_ca_handle
);
2053 i_nb_slots
= caps
.slot_num
;
2054 memset( p_sessions
, 0, sizeof(en50221_session_t
) * MAX_SESSIONS
);
2056 if( i_ca_type
& CA_CI_LINK
)
2058 ev_io_init(&cam_watcher
, en50221_Read
, i_ca_handle
, EV_READ
);
2059 ev_io_start(event_loop
, &cam_watcher
);
2061 ev_timer_init(&slot_watcher
, en50221_Poll
, CA_POLL_PERIOD
/ 1000000.,
2062 CA_POLL_PERIOD
/ 1000000.);
2063 ev_timer_start(event_loop
, &slot_watcher
);
2069 /*****************************************************************************
2070 * en50221_Reset : Reset the CAM for en50221
2071 *****************************************************************************/
2072 void en50221_Reset( void )
2074 memset( p_slots
, 0, sizeof(ci_slot_t
) * MAX_CI_SLOTS
);
2076 if( i_ca_type
& CA_CI_LINK
)
2079 for ( i_slot
= 0; i_slot
< i_nb_slots
; i_slot
++ )
2080 ResetSlot( i_slot
);
2084 struct ca_slot_info info
;
2085 system_ids_t
*p_ids
;
2089 /* We don't reset the CAM in that case because it's done by the
2091 if ( ioctl( i_ca_handle
, CA_GET_SLOT_INFO
, &info
) < 0 )
2093 msg_Err( NULL
, "en50221_Init: couldn't get slot info" );
2094 close( i_ca_handle
);
2098 if( info
.flags
== 0 )
2100 msg_Err( NULL
, "en50221_Init: no CAM inserted" );
2101 close( i_ca_handle
);
2106 /* Allocate a dummy sessions */
2107 p_sessions
[0].i_resource_id
= RI_CONDITIONAL_ACCESS_SUPPORT
;
2108 p_sessions
[0].pf_close
= ConditionalAccessClose
;
2109 if ( p_sessions
[0].p_sys
== NULL
)
2110 p_sessions
[0].p_sys
= malloc(sizeof(system_ids_t
));
2111 memset( p_sessions
[0].p_sys
, 0, sizeof(system_ids_t
) );
2112 p_ids
= (system_ids_t
*)p_sessions
[0].p_sys
;
2113 p_ids
->b_high_level
= 1;
2115 /* Get application info to find out which cam we are using and make
2116 sure everything is ready to play */
2118 ca_msg
.msg
[0] = ( AOT_APPLICATION_INFO
& 0xFF0000 ) >> 16;
2119 ca_msg
.msg
[1] = ( AOT_APPLICATION_INFO
& 0x00FF00 ) >> 8;
2120 ca_msg
.msg
[2] = ( AOT_APPLICATION_INFO
& 0x0000FF ) >> 0;
2121 memset( &ca_msg
.msg
[3], 0, 253 );
2122 APDUSend( NULL
, 1, AOT_APPLICATION_INFO_ENQ
, NULL
, 0 );
2123 if ( ioctl( i_ca_handle
, CA_GET_MSG
, &ca_msg
) < 0 )
2125 msg_Err( NULL
, "en50221_Init: failed getting message" );
2126 close( i_ca_handle
);
2131 #ifdef HLCI_WAIT_CAM_READY
2132 while( ca_msg
.msg
[8] == 0xff && ca_msg
.msg
[9] == 0xff )
2135 msg_Dbg( NULL
, "CAM: please wait" );
2136 APDUSend( NULL
, 1, AOT_APPLICATION_INFO_ENQ
, NULL
, 0 );
2138 ca_msg
.msg
[0] = ( AOT_APPLICATION_INFO
& 0xFF0000 ) >> 16;
2139 ca_msg
.msg
[1] = ( AOT_APPLICATION_INFO
& 0x00FF00 ) >> 8;
2140 ca_msg
.msg
[2] = ( AOT_APPLICATION_INFO
& 0x0000FF ) >> 0;
2141 memset( &ca_msg
.msg
[3], 0, 253 );
2142 if ( ioctl( i_ca_handle
, CA_GET_MSG
, &ca_msg
) < 0 )
2144 msg_Err( NULL
, "en50221_Init: failed getting message" );
2145 close( i_ca_handle
);
2149 msg_Dbg( NULL
, "en50221_Init: Got length: %d, tag: 0x%x", ca_msg
.length
, APDUGetTag( ca_msg
.msg
, ca_msg
.length
) );
2152 if( ca_msg
.msg
[8] == 0xff && ca_msg
.msg
[9] == 0xff )
2154 msg_Err( NULL
, "CAM returns garbage as application info!" );
2155 close( i_ca_handle
);
2160 msg_Dbg( NULL
, "found CAM %s using id 0x%x", &ca_msg
.msg
[12],
2161 (ca_msg
.msg
[8]<<8)|ca_msg
.msg
[9] );
2165 /*****************************************************************************
2166 * en50221_Read : Read the CAM for a TPDU
2167 *****************************************************************************/
2168 static void en50221_Read(struct ev_loop
*loop
, struct ev_io
*w
, int revents
)
2172 ev_timer_again(event_loop
, &slot_watcher
);
2175 /*****************************************************************************
2176 * en50221_Poll : Send a poll TPDU to the CAM
2177 *****************************************************************************/
2178 static void en50221_Poll(struct ev_loop
*loop
, struct ev_timer
*w
, int revents
)
2183 /* Check module status */
2184 for ( i_slot
= 0; i_slot
< i_nb_slots
; i_slot
++ )
2186 ci_slot_t
*p_slot
= &p_slots
[i_slot
];
2187 ca_slot_info_t sinfo
;
2190 if ( ioctl( i_ca_handle
, CA_GET_SLOT_INFO
, &sinfo
) != 0 )
2192 msg_Err( NULL
, "en50221_Poll: couldn't get info on slot %d",
2197 if ( !(sinfo
.flags
& CA_CI_MODULE_READY
) )
2199 if ( p_slot
->b_active
)
2201 msg_Dbg( NULL
, "en50221_Poll: slot %d has been removed",
2203 ResetSlot( i_slot
);
2206 else if ( !p_slot
->b_active
)
2208 if ( !p_slot
->b_expect_answer
)
2209 InitSlot( NULL
, i_slot
);
2213 /* Check if applications have data to send */
2214 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2216 en50221_session_t
*p_session
= &p_sessions
[i_session_id
- 1];
2217 if ( p_session
->i_resource_id
&& p_session
->pf_manage
!= NULL
2218 && !p_slots
[ p_session
->i_slot
].b_expect_answer
)
2219 p_session
->pf_manage( NULL
, i_session_id
);
2222 /* Now send the poll command to inactive slots */
2223 for ( i_slot
= 0; i_slot
< i_nb_slots
; i_slot
++ )
2225 ci_slot_t
*p_slot
= &p_slots
[i_slot
];
2227 if ( p_slot
->b_active
&& !p_slot
->b_expect_answer
)
2229 if ( TPDUSend( NULL
, i_slot
, T_DATA_LAST
, NULL
, 0 ) != 0 )
2231 msg_Warn( NULL
, "couldn't send TPDU, resetting slot %d",
2233 switch (i_print_type
) {
2236 "<EVENT type=\"reset\" cause=\"cam_error\" />\n");
2239 fprintf(print_fh
, "reset cause: cam_error\n");
2244 ResetSlot( i_slot
);
2250 /*****************************************************************************
2252 *****************************************************************************/
2253 void en50221_AddPMT( uint8_t *p_pmt
)
2257 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2258 if ( p_sessions
[i_session_id
- 1].i_resource_id
2259 == RI_CONDITIONAL_ACCESS_SUPPORT
)
2260 CAPMTAdd( NULL
, i_session_id
, p_pmt
);
2263 /*****************************************************************************
2264 * en50221_UpdatePMT :
2265 *****************************************************************************/
2266 void en50221_UpdatePMT( uint8_t *p_pmt
)
2270 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2271 if ( p_sessions
[i_session_id
- 1].i_resource_id
2272 == RI_CONDITIONAL_ACCESS_SUPPORT
)
2273 CAPMTUpdate( NULL
, i_session_id
, p_pmt
);
2276 /*****************************************************************************
2277 * en50221_DeletePMT :
2278 *****************************************************************************/
2279 void en50221_DeletePMT( uint8_t *p_pmt
)
2283 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2284 if ( p_sessions
[i_session_id
- 1].i_resource_id
2285 == RI_CONDITIONAL_ACCESS_SUPPORT
)
2286 CAPMTDelete( NULL
, i_session_id
, p_pmt
);
2289 /*****************************************************************************
2290 * en50221_StatusMMI :
2291 *****************************************************************************/
2292 uint8_t en50221_StatusMMI( uint8_t *p_answer
, ssize_t
*pi_size
)
2294 struct ret_mmi_status
*p_ret
= (struct ret_mmi_status
*)p_answer
;
2296 if ( ioctl( i_ca_handle
, CA_GET_CAP
, &p_ret
->caps
) != 0 )
2298 msg_Err( NULL
, "ioctl CA_GET_CAP failed (%s)", strerror(errno
) );
2302 *pi_size
= sizeof(struct ret_mmi_status
);
2303 return RET_MMI_STATUS
;
2306 /*****************************************************************************
2307 * en50221_StatusMMISlot :
2308 *****************************************************************************/
2309 uint8_t en50221_StatusMMISlot( uint8_t *p_buffer
, ssize_t i_size
,
2310 uint8_t *p_answer
, ssize_t
*pi_size
)
2313 struct ret_mmi_slot_status
*p_ret
= (struct ret_mmi_slot_status
*)p_answer
;
2315 if ( i_size
!= 1 ) return RET_HUH
;
2318 p_ret
->sinfo
.num
= i_slot
;
2319 if ( ioctl( i_ca_handle
, CA_GET_SLOT_INFO
, &p_ret
->sinfo
) != 0 )
2321 msg_Err( NULL
, "ioctl CA_GET_SLOT_INFO failed (%s)", strerror(errno
) );
2325 *pi_size
= sizeof(struct ret_mmi_slot_status
);
2326 return RET_MMI_SLOT_STATUS
;
2329 /*****************************************************************************
2331 *****************************************************************************/
2332 uint8_t en50221_OpenMMI( uint8_t *p_buffer
, ssize_t i_size
)
2336 if ( i_size
!= 1 ) return RET_HUH
;
2339 if( i_ca_type
& CA_CI_LINK
)
2342 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2344 if ( p_sessions
[i_session_id
- 1].i_resource_id
== RI_MMI
2345 && p_sessions
[i_session_id
- 1].i_slot
== i_slot
)
2348 "MMI menu is already opened on slot %d (session=%d)",
2349 i_slot
, i_session_id
);
2354 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2356 if ( p_sessions
[i_session_id
- 1].i_resource_id
2357 == RI_APPLICATION_INFORMATION
2358 && p_sessions
[i_session_id
- 1].i_slot
== i_slot
)
2360 ApplicationInformationEnterMenu( NULL
, i_session_id
);
2365 msg_Err( NULL
, "no application information on slot %d", i_slot
);
2370 msg_Err( NULL
, "MMI menu not supported" );
2375 /*****************************************************************************
2376 * en50221_CloseMMI :
2377 *****************************************************************************/
2378 uint8_t en50221_CloseMMI( uint8_t *p_buffer
, ssize_t i_size
)
2382 if ( i_size
!= 1 ) return RET_HUH
;
2385 if( i_ca_type
& CA_CI_LINK
)
2388 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2390 if ( p_sessions
[i_session_id
- 1].i_resource_id
== RI_MMI
2391 && p_sessions
[i_session_id
- 1].i_slot
== i_slot
)
2393 MMISendClose( NULL
, i_session_id
);
2398 msg_Warn( NULL
, "closing a non-existing MMI session on slot %d",
2404 msg_Err( NULL
, "MMI menu not supported" );
2409 /*****************************************************************************
2410 * en50221_GetMMIObject :
2411 *****************************************************************************/
2412 uint8_t en50221_GetMMIObject( uint8_t *p_buffer
, ssize_t i_size
,
2413 uint8_t *p_answer
, ssize_t
*pi_size
)
2415 int i_session_id
, i_slot
;
2416 struct ret_mmi_recv
*p_ret
= (struct ret_mmi_recv
*)p_answer
;
2418 if ( i_size
!= 1 ) return RET_HUH
;
2421 if ( p_slots
[i_slot
].b_mmi_expected
)
2422 return RET_MMI_WAIT
; /* data not yet available */
2424 p_ret
->object
.i_object_type
= EN50221_MMI_NONE
;
2425 *pi_size
= sizeof(struct ret_mmi_recv
);
2427 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2429 if ( p_sessions
[i_session_id
- 1].i_resource_id
== RI_MMI
2430 && p_sessions
[i_session_id
- 1].i_slot
== i_slot
)
2433 (mmi_t
*)p_sessions
[i_session_id
- 1].p_sys
;
2434 if ( p_mmi
== NULL
)
2437 return RET_ERR
; /* should not happen */
2440 *pi_size
= COMM_BUFFER_SIZE
- COMM_HEADER_SIZE
-
2441 ((void *)&p_ret
->object
- (void *)p_ret
);
2442 if ( en50221_SerializeMMIObject( (uint8_t *)&p_ret
->object
,
2443 pi_size
, &p_mmi
->last_object
) == -1 )
2446 msg_Err( NULL
, "MMI structure too big" );
2449 *pi_size
+= ((void *)&p_ret
->object
- (void *)p_ret
);
2454 return RET_MMI_RECV
;
2458 /*****************************************************************************
2459 * en50221_SendMMIObject :
2460 *****************************************************************************/
2461 uint8_t en50221_SendMMIObject( uint8_t *p_buffer
, ssize_t i_size
)
2463 int i_session_id
, i_slot
;
2464 struct cmd_mmi_send
*p_cmd
= (struct cmd_mmi_send
*)p_buffer
;
2466 if ( i_size
< sizeof(struct cmd_mmi_send
))
2468 msg_Err( NULL
, "command packet too short (%zd)\n", i_size
);
2472 if ( en50221_UnserializeMMIObject( &p_cmd
->object
, i_size
-
2473 ((void *)&p_cmd
->object
- (void *)p_cmd
) ) == -1 )
2476 i_slot
= p_cmd
->i_slot
;
2478 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2480 if ( p_sessions
[i_session_id
- 1].i_resource_id
== RI_MMI
2481 && p_sessions
[i_session_id
- 1].i_slot
== i_slot
)
2483 MMISendObject( NULL
, i_session_id
, &p_cmd
->object
);
2488 msg_Err( NULL
, "SendMMIObject when no MMI session is opened !" );
2493 #include <inttypes.h>
2495 int i_ca_handle
= 0;
2498 void en50221_AddPMT( uint8_t *p_pmt
) { };
2499 void en50221_UpdatePMT( uint8_t *p_pmt
) { };
2500 void en50221_DeletePMT( uint8_t *p_pmt
) { };
2501 void en50221_Reset( void ) { };