1 /*****************************************************************************
2 * en50221.c : implementation of the transport, session and applications
4 *****************************************************************************
5 * Copyright (C) 2004-2005, 2010 VideoLAN
8 * Authors: Christophe Massiot <massiot@via.ecp.fr>
9 * Based on code from libdvbci Copyright (C) 2000 Klaus Schmidinger
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
24 *****************************************************************************/
33 #include <sys/types.h>
36 #include <sys/ioctl.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
44 /* DVB Card Drivers */
45 #include <linux/dvb/version.h>
46 #include <linux/dvb/dmx.h>
47 #include <linux/dvb/frontend.h>
48 #include <linux/dvb/ca.h>
50 #include <bitstream/mpeg/psi.h>
51 #include <bitstream/dvb/ci.h>
52 #include <bitstream/dvb/si.h>
58 #define TAB_APPEND( count, tab, p ) \
61 (tab) = realloc( tab, sizeof( void ** ) * ( (count) + 1 ) ); \
65 (tab) = malloc( sizeof( void ** ) ); \
70 /*****************************************************************************
72 *****************************************************************************/
74 #define CAM_INIT_TIMEOUT 15000000 /* 15 s */
75 #undef HLCI_WAIT_CAM_READY
76 #define CAPMT_WAIT 100 /* ms */
78 typedef struct en50221_msg_t
82 struct en50221_msg_t
*p_next
;
85 typedef struct en50221_session_t
89 void (* pf_handle
)( access_t
*, int, uint8_t *, int );
90 void (* pf_close
)( access_t
*, int );
91 void (* pf_manage
)( access_t
*, int );
95 typedef struct ci_slot_t
101 bool b_mmi_undisplayed
;
104 en50221_msg_t
*p_recv
;
107 en50221_msg_t
*p_send
;
108 en50221_msg_t
**pp_send_last
;
111 /* InitSlot callback, if not 0 */
112 mtime_t i_init_timeout
;
114 /* SPDUSend callback, if p_spdu is not NULL */
115 /* SessionOpen callback, if not 0 */
116 int i_pending_session_id
;
122 static int i_nb_slots
= 0;
123 static ci_slot_t p_slots
[MAX_CI_SLOTS
];
124 static en50221_session_t p_sessions
[MAX_SESSIONS
];
126 /*****************************************************************************
128 *****************************************************************************/
129 static void SessionOpenCb( access_t
*p_access
, uint8_t i_slot
);
130 static void SPDUHandle( access_t
* p_access
, uint8_t i_slot
,
131 uint8_t *p_spdu
, int i_size
);
133 static void ResourceManagerOpen( access_t
* p_access
, int i_session_id
);
134 static void ApplicationInformationOpen( access_t
* p_access
, int i_session_id
);
135 static void ConditionalAccessOpen( access_t
* p_access
, int i_session_id
);
136 static void DateTimeOpen( access_t
* p_access
, int i_session_id
);
137 static void MMIOpen( access_t
* p_access
, int i_session_id
);
139 /*****************************************************************************
141 *****************************************************************************/
142 #define SIZE_INDICATOR 0x80
144 static uint8_t *GetLength( uint8_t *p_data
, int *pi_length
)
146 *pi_length
= *p_data
++;
148 if ( (*pi_length
& SIZE_INDICATOR
) != 0 )
150 int l
= *pi_length
& ~SIZE_INDICATOR
;
154 for ( i
= 0; i
< l
; i
++ )
155 *pi_length
= (*pi_length
<< 8) | *p_data
++;
161 static uint8_t *SetLength( uint8_t *p_data
, int i_length
)
165 if ( i_length
< 128 )
169 else if ( i_length
< 256 )
171 *p
++ = SIZE_INDICATOR
| 0x1;
174 else if ( i_length
< 65536 )
176 *p
++ = SIZE_INDICATOR
| 0x2;
177 *p
++ = i_length
>> 8;
178 *p
++ = i_length
& 0xff;
180 else if ( i_length
< 16777216 )
182 *p
++ = SIZE_INDICATOR
| 0x3;
183 *p
++ = i_length
>> 16;
184 *p
++ = (i_length
>> 8) & 0xff;
185 *p
++ = i_length
& 0xff;
189 *p
++ = SIZE_INDICATOR
| 0x4;
190 *p
++ = i_length
>> 24;
191 *p
++ = (i_length
>> 16) & 0xff;
192 *p
++ = (i_length
>> 8) & 0xff;
193 *p
++ = i_length
& 0xff;
204 #define MAX_TPDU_SIZE 4096
205 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
207 #define DATA_INDICATOR 0x80
211 #define T_CREATE_TC 0x82
212 #define T_CTC_REPLY 0x83
213 #define T_DELETE_TC 0x84
214 #define T_DTC_REPLY 0x85
215 #define T_REQUEST_TC 0x86
216 #define T_NEW_TC 0x87
217 #define T_TC_ERROR 0x88
218 #define T_DATA_LAST 0xA0
219 #define T_DATA_MORE 0xA1
221 static void Dump( bool b_outgoing
, uint8_t *p_data
, int i_size
)
226 fprintf(stderr
, "%s ", b_outgoing
? "-->" : "<--");
227 for ( i
= 0; i
< i_size
&& i
< MAX_DUMP
; i
++)
228 fprintf(stderr
, "%02X ", p_data
[i
]);
229 fprintf(stderr
, "%s\n", i_size
>= MAX_DUMP
? "..." : "");
233 /*****************************************************************************
235 *****************************************************************************/
236 static int TPDUWrite( access_t
* p_access
, uint8_t i_slot
)
238 ci_slot_t
*p_slot
= &p_slots
[i_slot
];
239 en50221_msg_t
*p_send
= p_slot
->p_send
;
241 if ( p_slot
->b_expect_answer
)
243 "en50221: writing while expecting an answer on slot %u",
245 if ( p_send
== NULL
)
247 msg_Warn( p_access
, "en50221: no data to write on slot %u !", i_slot
);
250 p_slot
->p_send
= p_send
->p_next
;
251 if ( p_slot
->p_send
== NULL
)
252 p_slot
->pp_send_last
= &p_slot
->p_send
;
254 Dump( true, p_send
->p_data
, p_send
->i_size
);
256 if ( write( i_ca_handle
, p_send
->p_data
, p_send
->i_size
)
259 msg_Err( p_access
, "en50221: cannot write to CAM device (%m)" );
260 free( p_send
->p_data
);
265 free( p_send
->p_data
);
267 p_slot
->b_expect_answer
= true;
272 /*****************************************************************************
274 *****************************************************************************/
275 static int TPDUSend( access_t
* p_access
, uint8_t i_slot
, uint8_t i_tag
,
276 const uint8_t *p_content
, int i_length
)
278 ci_slot_t
*p_slot
= &p_slots
[i_slot
];
279 uint8_t i_tcid
= i_slot
+ 1;
280 en50221_msg_t
*p_send
= malloc( sizeof(en50221_msg_t
) );
281 uint8_t *p_data
= malloc( MAX_TPDU_SIZE
);
297 p_data
[3] = 1; /* length */
304 p_data
[3] = 2; /* length */
306 p_data
[5] = p_content
[0];
313 /* i_length <= MAX_TPDU_DATA */
314 uint8_t *p
= p_data
+ 3;
315 p
= SetLength( p
, i_length
+ 1 );
319 memcpy( p
, p_content
, i_length
);
320 i_size
= i_length
+ (p
- p_data
);
328 p_send
->p_data
= p_data
;
329 p_send
->i_size
= i_size
;
330 p_send
->p_next
= NULL
;
332 *p_slot
->pp_send_last
= p_send
;
333 p_slot
->pp_send_last
= &p_send
->p_next
;
335 if ( !p_slot
->b_expect_answer
)
336 return TPDUWrite( p_access
, i_slot
);
342 /*****************************************************************************
344 *****************************************************************************/
345 static int TPDURecv( access_t
* p_access
)
348 uint8_t i_tag
, i_slot
;
349 uint8_t p_data
[MAX_TPDU_SIZE
];
355 i_size
= read( i_ca_handle
, p_data
, MAX_TPDU_SIZE
);
357 while ( i_size
< 0 && errno
== EINTR
);
361 msg_Err( p_access
, "en50221: cannot read from CAM device (%d:%m)",
366 Dump( false, p_data
, i_size
);
368 i_slot
= p_data
[1] - 1;
371 if ( i_slot
>= i_nb_slots
)
373 msg_Warn( p_access
, "en50221: TPDU is from an unknown slot %u",
377 p_slot
= &p_slots
[i_slot
];
379 p_slot
->b_has_data
= !!(p_data
[i_size
- 4] == T_SB
380 && p_data
[i_size
- 3] == 2
381 && (p_data
[i_size
- 1] & DATA_INDICATOR
));
382 p_slot
->b_expect_answer
= false;
387 p_slot
->b_active
= true;
388 p_slot
->i_init_timeout
= 0;
389 msg_Dbg( p_access
, "CI slot %d is active", i_slot
);
397 /* intended pass-through */
400 en50221_msg_t
*p_recv
;
402 uint8_t *p_session
= GetLength( &p_data
[3], &i_session_size
);
404 if ( i_session_size
<= 1 )
409 if ( p_slot
->p_recv
== NULL
)
411 p_slot
->p_recv
= malloc( sizeof(en50221_msg_t
) );
412 p_slot
->p_recv
->p_data
= NULL
;
413 p_slot
->p_recv
->i_size
= 0;
416 p_recv
= p_slot
->p_recv
;
417 p_recv
->p_data
= realloc( p_recv
->p_data
,
418 p_recv
->i_size
+ i_session_size
);
419 memcpy( &p_recv
->p_data
[ p_recv
->i_size
], p_session
, i_session_size
);
420 p_recv
->i_size
+= i_session_size
;
424 SPDUHandle( p_access
, i_slot
, p_recv
->p_data
, p_recv
->i_size
);
425 free( p_recv
->p_data
);
427 p_slot
->p_recv
= NULL
;
433 msg_Warn( p_access
, "en50221: unhandled R_TPDU tag %u slot %u", i_tag
,
438 if ( !p_slot
->b_expect_answer
&& p_slot
->p_send
!= NULL
)
439 TPDUWrite( p_access
, i_slot
);
440 if ( !p_slot
->b_expect_answer
&& p_slot
->i_pending_session_id
!= 0 )
441 SessionOpenCb( p_access
, i_slot
);
442 if ( !p_slot
->b_expect_answer
&& p_slot
->b_has_data
)
443 TPDUSend( p_access
, i_slot
, T_RCV
, NULL
, 0 );
453 #define ST_SESSION_NUMBER 0x90
454 #define ST_OPEN_SESSION_REQUEST 0x91
455 #define ST_OPEN_SESSION_RESPONSE 0x92
456 #define ST_CREATE_SESSION 0x93
457 #define ST_CREATE_SESSION_RESPONSE 0x94
458 #define ST_CLOSE_SESSION_REQUEST 0x95
459 #define ST_CLOSE_SESSION_RESPONSE 0x96
462 #define SS_NOT_ALLOCATED 0xF0
464 #define RI_RESOURCE_MANAGER 0x00010041
465 #define RI_APPLICATION_INFORMATION 0x00020041
466 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
467 #define RI_HOST_CONTROL 0x00200041
468 #define RI_DATE_TIME 0x00240041
469 #define RI_MMI 0x00400041
471 static int ResourceIdToInt( uint8_t *p_data
)
473 return ((int)p_data
[0] << 24) | ((int)p_data
[1] << 16)
474 | ((int)p_data
[2] << 8) | p_data
[3];
477 /*****************************************************************************
479 *****************************************************************************/
480 static int SPDUSend( access_t
*p_access
, int i_session_id
,
481 uint8_t *p_data
, int i_size
)
483 uint8_t *p_spdu
= malloc( i_size
+ 4 );
485 uint8_t i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
487 *p
++ = ST_SESSION_NUMBER
;
489 *p
++ = (i_session_id
>> 8);
490 *p
++ = i_session_id
& 0xff;
492 memcpy( p
, p_data
, i_size
);
499 if ( i_size
> MAX_TPDU_DATA
)
501 if ( TPDUSend( p_access
, i_slot
, T_DATA_MORE
, p
,
502 MAX_TPDU_DATA
) != 0 )
504 msg_Err( p_access
, "couldn't send TPDU on session %d",
510 i_size
-= MAX_TPDU_DATA
;
514 if ( TPDUSend( p_access
, i_slot
, T_DATA_LAST
, p
, i_size
)
517 msg_Err( p_access
, "couldn't send TPDU on session %d",
530 /*****************************************************************************
532 *****************************************************************************/
533 static void SessionOpenCb( access_t
*p_access
, uint8_t i_slot
)
535 ci_slot_t
*p_slot
= &p_slots
[i_slot
];
536 int i_session_id
= p_slot
->i_pending_session_id
;
537 int i_resource_id
= p_sessions
[i_session_id
- 1].i_resource_id
;
539 p_slot
->i_pending_session_id
= 0;
541 switch ( i_resource_id
)
543 case RI_RESOURCE_MANAGER
:
544 ResourceManagerOpen( p_access
, i_session_id
); break;
545 case RI_APPLICATION_INFORMATION
:
546 ApplicationInformationOpen( p_access
, i_session_id
); break;
547 case RI_CONDITIONAL_ACCESS_SUPPORT
:
548 ConditionalAccessOpen( p_access
, i_session_id
); break;
550 DateTimeOpen( p_access
, i_session_id
); break;
552 MMIOpen( p_access
, i_session_id
); break;
554 case RI_HOST_CONTROL
:
556 msg_Err( p_access
, "unknown resource id (0x%x)", i_resource_id
);
557 p_sessions
[i_session_id
- 1].i_resource_id
= 0;
561 static void SessionOpen( access_t
* p_access
, uint8_t i_slot
,
562 uint8_t *p_spdu
, int i_size
)
564 ci_slot_t
*p_slot
= &p_slots
[i_slot
];
566 int i_resource_id
= ResourceIdToInt( &p_spdu
[2] );
567 uint8_t p_response
[16];
568 int i_status
= SS_NOT_ALLOCATED
;
570 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
572 if ( !p_sessions
[i_session_id
- 1].i_resource_id
)
575 if ( i_session_id
== MAX_SESSIONS
)
577 msg_Err( p_access
, "too many sessions !" );
580 p_sessions
[i_session_id
- 1].i_slot
= i_slot
;
581 p_sessions
[i_session_id
- 1].i_resource_id
= i_resource_id
;
582 p_sessions
[i_session_id
- 1].pf_close
= NULL
;
583 p_sessions
[i_session_id
- 1].pf_manage
= NULL
;
585 if ( i_resource_id
== RI_RESOURCE_MANAGER
586 || i_resource_id
== RI_APPLICATION_INFORMATION
587 || i_resource_id
== RI_CONDITIONAL_ACCESS_SUPPORT
588 || i_resource_id
== RI_DATE_TIME
589 || i_resource_id
== RI_MMI
)
594 p_response
[0] = ST_OPEN_SESSION_RESPONSE
;
596 p_response
[2] = i_status
;
597 p_response
[3] = p_spdu
[2];
598 p_response
[4] = p_spdu
[3];
599 p_response
[5] = p_spdu
[4];
600 p_response
[6] = p_spdu
[5];
601 p_response
[7] = i_session_id
>> 8;
602 p_response
[8] = i_session_id
& 0xff;
604 if ( TPDUSend( p_access
, i_slot
, T_DATA_LAST
, p_response
, 9 ) != 0 )
607 "SessionOpen: couldn't send TPDU on slot %d", i_slot
);
611 if ( p_slot
->i_pending_session_id
!= 0 )
612 msg_Warn( p_access
, "overwriting pending session %d",
613 p_slot
->i_pending_session_id
);
614 p_slot
->i_pending_session_id
= i_session_id
;
618 /* unused code for the moment - commented out to keep gcc happy */
619 /*****************************************************************************
621 *****************************************************************************/
622 static void SessionCreate( access_t
* p_access
, int i_slot
, int i_resource_id
)
624 uint8_t p_response
[16];
628 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
630 if ( !p_sessions
[i_session_id
- 1].i_resource_id
)
633 if ( i_session_id
== MAX_SESSIONS
)
635 msg_Err( p_access
, "too many sessions !" );
638 p_sessions
[i_session_id
- 1].i_slot
= i_slot
;
639 p_sessions
[i_session_id
- 1].i_resource_id
= i_resource_id
;
640 p_sessions
[i_session_id
- 1].pf_close
= NULL
;
641 p_sessions
[i_session_id
- 1].pf_manage
= NULL
;
642 p_sessions
[i_session_id
- 1].p_sys
= NULL
;
644 p_response
[0] = ST_CREATE_SESSION
;
646 p_response
[2] = i_resource_id
>> 24;
647 p_response
[3] = (i_resource_id
>> 16) & 0xff;
648 p_response
[4] = (i_resource_id
>> 8) & 0xff;
649 p_response
[5] = i_resource_id
& 0xff;
650 p_response
[6] = i_session_id
>> 8;
651 p_response
[7] = i_session_id
& 0xff;
653 if ( TPDUSend( p_access
, i_slot
, T_DATA_LAST
, p_response
, 4 ) !=
657 "SessionCreate: couldn't send TPDU on slot %d", i_slot
);
663 /*****************************************************************************
664 * SessionCreateResponse
665 *****************************************************************************/
666 static void SessionCreateResponse( access_t
* p_access
, uint8_t i_slot
,
667 uint8_t *p_spdu
, int i_size
)
669 int i_status
= p_spdu
[2];
670 int i_resource_id
= ResourceIdToInt( &p_spdu
[3] );
671 int i_session_id
= ((int)p_spdu
[7] << 8) | p_spdu
[8];
673 if ( i_status
!= SS_OK
)
675 msg_Err( p_access
, "SessionCreateResponse: failed to open session %d"
676 " resource=0x%x status=0x%x", i_session_id
, i_resource_id
,
678 p_sessions
[i_session_id
- 1].i_resource_id
= 0;
682 switch ( i_resource_id
)
684 case RI_RESOURCE_MANAGER
:
685 ResourceManagerOpen( p_access
, i_session_id
); break;
686 case RI_APPLICATION_INFORMATION
:
687 ApplicationInformationOpen( p_access
, i_session_id
); break;
688 case RI_CONDITIONAL_ACCESS_SUPPORT
:
689 ConditionalAccessOpen( p_access
, i_session_id
); break;
691 DateTimeOpen( p_access
, i_session_id
); break;
693 MMIOpen( p_access
, i_session_id
); break;
695 case RI_HOST_CONTROL
:
697 msg_Err( p_access
, "unknown resource id (0x%x)", i_resource_id
);
698 p_sessions
[i_session_id
- 1].i_resource_id
= 0;
702 /*****************************************************************************
704 *****************************************************************************/
705 static void SessionSendClose( access_t
* p_access
, int i_session_id
)
707 uint8_t p_response
[16];
708 uint8_t i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
710 p_response
[0] = ST_CLOSE_SESSION_REQUEST
;
712 p_response
[2] = i_session_id
>> 8;
713 p_response
[3] = i_session_id
& 0xff;
715 if ( TPDUSend( p_access
, i_slot
, T_DATA_LAST
, p_response
, 4 ) !=
719 "SessionSendClose: couldn't send TPDU on slot %d", i_slot
);
724 /*****************************************************************************
726 *****************************************************************************/
727 static void SessionClose( access_t
* p_access
, int i_session_id
)
729 uint8_t p_response
[16];
730 uint8_t i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
732 if ( p_sessions
[i_session_id
- 1].pf_close
!= NULL
)
733 p_sessions
[i_session_id
- 1].pf_close( p_access
, i_session_id
);
734 p_sessions
[i_session_id
- 1].i_resource_id
= 0;
736 p_response
[0] = ST_CLOSE_SESSION_RESPONSE
;
738 p_response
[2] = SS_OK
;
739 p_response
[3] = i_session_id
>> 8;
740 p_response
[4] = i_session_id
& 0xff;
742 if ( TPDUSend( p_access
, i_slot
, T_DATA_LAST
, p_response
, 5 ) !=
746 "SessionClose: couldn't send TPDU on slot %d", i_slot
);
751 /*****************************************************************************
753 *****************************************************************************/
754 static void SPDUHandle( access_t
* p_access
, uint8_t i_slot
,
755 uint8_t *p_spdu
, int i_size
)
761 case ST_SESSION_NUMBER
:
764 i_session_id
= ((int)p_spdu
[2] << 8) | p_spdu
[3];
765 p_sessions
[i_session_id
- 1].pf_handle( p_access
, i_session_id
,
766 p_spdu
+ 4, i_size
- 4 );
769 case ST_OPEN_SESSION_REQUEST
:
770 if ( i_size
!= 6 || p_spdu
[1] != 0x4 )
772 SessionOpen( p_access
, i_slot
, p_spdu
, i_size
);
775 case ST_CREATE_SESSION_RESPONSE
:
776 if ( i_size
!= 9 || p_spdu
[1] != 0x7 )
778 SessionCreateResponse( p_access
, i_slot
, p_spdu
, i_size
);
781 case ST_CLOSE_SESSION_REQUEST
:
782 if ( i_size
!= 4 || p_spdu
[1] != 0x2 )
784 i_session_id
= ((int)p_spdu
[2] << 8) | p_spdu
[3];
785 SessionClose( p_access
, i_session_id
);
788 case ST_CLOSE_SESSION_RESPONSE
:
789 if ( i_size
!= 5 || p_spdu
[1] != 0x3 )
791 i_session_id
= ((int)p_spdu
[3] << 8) | p_spdu
[4];
794 msg_Err( p_access
, "closing a session which is not allocated (%d)",
799 if ( p_sessions
[i_session_id
- 1].pf_close
!= NULL
)
800 p_sessions
[i_session_id
- 1].pf_close( p_access
,
802 p_sessions
[i_session_id
- 1].i_resource_id
= 0;
807 msg_Err( p_access
, "unexpected tag in SPDUHandle (%x)", p_spdu
[0] );
817 #define AOT_NONE 0x000000
818 #define AOT_PROFILE_ENQ 0x9F8010
819 #define AOT_PROFILE 0x9F8011
820 #define AOT_PROFILE_CHANGE 0x9F8012
821 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
822 #define AOT_APPLICATION_INFO 0x9F8021
823 #define AOT_ENTER_MENU 0x9F8022
824 #define AOT_CA_INFO_ENQ 0x9F8030
825 #define AOT_CA_INFO 0x9F8031
826 #define AOT_CA_PMT 0x9F8032
827 #define AOT_CA_PMT_REPLY 0x9F8033
828 #define AOT_CA_UPDATE 0x9F8034
829 #define AOT_TUNE 0x9F8400
830 #define AOT_REPLACE 0x9F8401
831 #define AOT_CLEAR_REPLACE 0x9F8402
832 #define AOT_ASK_RELEASE 0x9F8403
833 #define AOT_DATE_TIME_ENQ 0x9F8440
834 #define AOT_DATE_TIME 0x9F8441
835 #define AOT_CLOSE_MMI 0x9F8800
836 #define AOT_DISPLAY_CONTROL 0x9F8801
837 #define AOT_DISPLAY_REPLY 0x9F8802
838 #define AOT_TEXT_LAST 0x9F8803
839 #define AOT_TEXT_MORE 0x9F8804
840 #define AOT_KEYPAD_CONTROL 0x9F8805
841 #define AOT_KEYPRESS 0x9F8806
842 #define AOT_ENQ 0x9F8807
843 #define AOT_ANSW 0x9F8808
844 #define AOT_MENU_LAST 0x9F8809
845 #define AOT_MENU_MORE 0x9F880A
846 #define AOT_MENU_ANSW 0x9F880B
847 #define AOT_LIST_LAST 0x9F880C
848 #define AOT_LIST_MORE 0x9F880D
849 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
850 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
851 #define AOT_DISPLAY_MESSAGE 0x9F8810
852 #define AOT_SCENE_END_MARK 0x9F8811
853 #define AOT_SCENE_DONE 0x9F8812
854 #define AOT_SCENE_CONTROL 0x9F8813
855 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
856 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
857 #define AOT_FLUSH_DOWNLOAD 0x9F8816
858 #define AOT_DOWNLOAD_REPLY 0x9F8817
859 #define AOT_COMMS_CMD 0x9F8C00
860 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
861 #define AOT_COMMS_REPLY 0x9F8C02
862 #define AOT_COMMS_SEND_LAST 0x9F8C03
863 #define AOT_COMMS_SEND_MORE 0x9F8C04
864 #define AOT_COMMS_RCV_LAST 0x9F8C05
865 #define AOT_COMMS_RCV_MORE 0x9F8C06
867 /*****************************************************************************
869 *****************************************************************************/
870 static int APDUGetTag( const uint8_t *p_apdu
, int i_size
)
875 for ( i
= 0; i
< 3; i
++ )
876 t
= (t
<< 8) | *p_apdu
++;
883 /*****************************************************************************
885 *****************************************************************************/
886 static uint8_t *APDUGetLength( uint8_t *p_apdu
, int *pi_size
)
888 return GetLength( &p_apdu
[3], pi_size
);
891 /*****************************************************************************
893 *****************************************************************************/
894 static int APDUSend( access_t
* p_access
, int i_session_id
, int i_tag
,
895 uint8_t *p_data
, int i_size
)
897 uint8_t *p_apdu
= malloc( i_size
+ 12 );
902 *p
++ = (i_tag
>> 16);
903 *p
++ = (i_tag
>> 8) & 0xff;
905 p
= SetLength( p
, i_size
);
907 memcpy( p
, p_data
, i_size
);
908 if ( i_ca_type
== CA_CI_LINK
)
910 i_ret
= SPDUSend( p_access
, i_session_id
, p_apdu
, i_size
+ p
- p_apdu
);
914 if ( i_size
+ p
- p_apdu
> 256 )
916 msg_Err( p_access
, "CAM: apdu overflow" );
921 ca_msg
.length
= i_size
+ p
- p_apdu
;
922 if ( i_size
== 0 ) ca_msg
.length
=3;
923 memcpy( ca_msg
.msg
, p_apdu
, i_size
+ p
- p_apdu
);
924 i_ret
= ioctl(i_ca_handle
, CA_SEND_MSG
, &ca_msg
);
927 msg_Err( p_access
, "Error sending to CAM: %m" );
940 /*****************************************************************************
941 * ResourceManagerHandle
942 *****************************************************************************/
943 static void ResourceManagerHandle( access_t
* p_access
, int i_session_id
,
944 uint8_t *p_apdu
, int i_size
)
946 int i_tag
= APDUGetTag( p_apdu
, i_size
);
950 case AOT_PROFILE_ENQ
:
952 int resources
[] = { htonl(RI_RESOURCE_MANAGER
),
953 htonl(RI_APPLICATION_INFORMATION
),
954 htonl(RI_CONDITIONAL_ACCESS_SUPPORT
),
958 APDUSend( p_access
, i_session_id
, AOT_PROFILE
, (uint8_t*)resources
,
963 APDUSend( p_access
, i_session_id
, AOT_PROFILE_CHANGE
, NULL
, 0 );
967 msg_Err( p_access
, "unexpected tag in ResourceManagerHandle (0x%x)",
972 /*****************************************************************************
973 * ResourceManagerOpen
974 *****************************************************************************/
975 static void ResourceManagerOpen( access_t
* p_access
, int i_session_id
)
978 msg_Dbg( p_access
, "opening ResourceManager session (%d)", i_session_id
);
980 p_sessions
[i_session_id
- 1].pf_handle
= ResourceManagerHandle
;
982 APDUSend( p_access
, i_session_id
, AOT_PROFILE_ENQ
, NULL
, 0 );
986 * Application Information
989 /*****************************************************************************
990 * ApplicationInformationEnterMenu
991 *****************************************************************************/
992 static void ApplicationInformationEnterMenu( access_t
* p_access
,
995 int i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
997 msg_Dbg( p_access
, "entering MMI menus on session %d", i_session_id
);
998 APDUSend( p_access
, i_session_id
, AOT_ENTER_MENU
, NULL
, 0 );
999 p_slots
[i_slot
].b_mmi_expected
= true;
1002 /*****************************************************************************
1003 * ApplicationInformationHandle
1004 *****************************************************************************/
1005 static void ApplicationInformationHandle( access_t
* p_access
, int i_session_id
,
1006 uint8_t *p_apdu
, int i_size
)
1008 int i_tag
= APDUGetTag( p_apdu
, i_size
);
1012 case AOT_APPLICATION_INFO
:
1014 int i_type
, i_manufacturer
, i_code
;
1016 uint8_t *d
= APDUGetLength( p_apdu
, &l
);
1021 i_manufacturer
= ((int)d
[0] << 8) | d
[1];
1023 i_code
= ((int)d
[0] << 8) | d
[1];
1025 d
= GetLength( d
, &l
);
1028 char *psz_name
= malloc(l
+ 1);
1029 memcpy( psz_name
, d
, l
);
1031 msg_Info( p_access
, "CAM: %s, %02X, %04X, %04X",
1032 psz_name
, i_type
, i_manufacturer
, i_code
);
1033 switch (i_print_type
)
1036 psz_name
= dvb_string_xml_escape(psz_name
);
1037 printf("<STATUS type=\"cam\" status=\"1\" cam_name=\"%s\" cam_type=\"%d\" cam_manufacturer=\"%d\" cam_product=\"%d\" />\n",
1038 psz_name
, i_type
, i_manufacturer
, i_code
);
1041 printf("CAM: name=%s type=%d manufacturer=%d product=%d\n",
1042 psz_name
, i_type
, i_manufacturer
, i_code
);
1050 "unexpected tag in ApplicationInformationHandle (0x%x)",
1055 /*****************************************************************************
1056 * ApplicationInformationOpen
1057 *****************************************************************************/
1058 static void ApplicationInformationOpen( access_t
* p_access
, int i_session_id
)
1061 msg_Dbg( p_access
, "opening ApplicationInformation session (%d)", i_session_id
);
1063 p_sessions
[i_session_id
- 1].pf_handle
= ApplicationInformationHandle
;
1065 APDUSend( p_access
, i_session_id
, AOT_APPLICATION_INFO_ENQ
, NULL
, 0 );
1069 * Conditional Access
1074 int i_nb_system_ids
;
1075 uint16_t *pi_system_ids
;
1077 int i_selected_programs
;
1081 static bool CheckSystemID( system_ids_t
*p_ids
, uint16_t i_id
)
1084 if( p_ids
== NULL
) return false;
1085 if( p_ids
->b_high_level
) return true;
1087 for ( i
= 0; i
< p_ids
->i_nb_system_ids
; i
++ )
1088 if ( p_ids
->pi_system_ids
[i
] == i_id
)
1094 /*****************************************************************************
1096 *****************************************************************************/
1097 static bool HasCADescriptors( system_ids_t
*p_ids
, uint8_t *p_descs
)
1099 const uint8_t *p_desc
;
1102 while ( (p_desc
= descs_get_desc( p_descs
, j
)) != NULL
)
1104 uint8_t i_tag
= desc_get_tag( p_desc
);
1107 if ( i_tag
== 0x9 && desc09_validate( p_desc
)
1108 && CheckSystemID( p_ids
, desc09_get_sysid( p_desc
) ) )
1115 static void CopyCADescriptors( system_ids_t
*p_ids
, uint8_t i_cmd
,
1116 uint8_t *p_infos
, uint8_t *p_descs
)
1118 const uint8_t *p_desc
;
1119 uint16_t j
= 0, k
= 0;
1121 capmti_init( p_infos
);
1122 capmti_set_length( p_infos
, 0xfff );
1123 capmti_set_cmd( p_infos
, i_cmd
);
1125 while ( (p_desc
= descs_get_desc( p_descs
, j
)) != NULL
)
1127 uint8_t i_tag
= desc_get_tag( p_desc
);
1130 if ( i_tag
== 0x9 && desc09_validate( p_desc
)
1131 && CheckSystemID( p_ids
, desc09_get_sysid( p_desc
) ) )
1133 uint8_t *p_info
= capmti_get_info( p_infos
, k
);
1135 memcpy( p_info
, p_desc
,
1136 DESC_HEADER_SIZE
+ desc_get_length( p_desc
) );
1142 uint8_t *p_info
= capmti_get_info( p_infos
, k
);
1143 capmti_set_length( p_infos
, p_info
- p_infos
- DESCS_HEADER_SIZE
);
1146 capmti_set_length( p_infos
, 0 );
1149 static uint8_t *CAPMTBuild( access_t
* p_access
, int i_session_id
,
1150 uint8_t *p_pmt
, uint8_t i_list_mgt
,
1151 uint8_t i_cmd
, int *pi_capmt_size
)
1153 system_ids_t
*p_ids
=
1154 (system_ids_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1156 uint8_t *p_capmt
, *p_capmt_n
;
1158 bool b_has_ca
= HasCADescriptors( p_ids
, pmt_get_descs( p_pmt
) );
1159 bool b_has_es
= false;
1162 while ( (p_es
= pmt_get_es( p_pmt
, j
)) != NULL
)
1164 uint16_t i_pid
= pmtn_get_pid( p_es
);
1167 if ( demux_PIDIsSelected( i_pid
) )
1171 || HasCADescriptors( p_ids
, pmtn_get_descs( p_es
) );
1184 "no compatible scrambling system for SID %d on session %d",
1185 pmt_get_program( p_pmt
), i_session_id
);
1190 p_capmt
= capmt_allocate();
1191 capmt_init( p_capmt
);
1192 capmt_set_listmanagement( p_capmt
, i_list_mgt
);
1193 capmt_set_program( p_capmt
, pmt_get_program( p_pmt
) );
1194 capmt_set_version( p_capmt
, psi_get_version( p_pmt
) );
1196 CopyCADescriptors( p_ids
, i_cmd
, capmt_get_infos( p_capmt
),
1197 pmt_get_descs( p_pmt
) );
1200 while ( (p_es
= pmt_get_es( p_pmt
, j
)) != NULL
)
1202 uint16_t i_pid
= pmtn_get_pid( p_es
);
1205 if ( !demux_PIDIsSelected( i_pid
) )
1208 p_capmt_n
= capmt_get_es( p_capmt
, k
);
1211 capmtn_init( p_capmt_n
);
1212 capmtn_set_streamtype( p_capmt_n
, pmtn_get_streamtype( p_es
) );
1213 capmtn_set_pid( p_capmt_n
, pmtn_get_pid( p_es
) );
1215 CopyCADescriptors( p_ids
, i_cmd
, capmtn_get_infos( p_capmt_n
),
1216 pmtn_get_descs( p_es
) );
1219 p_capmt_n
= capmt_get_es( p_capmt
, k
);
1220 *pi_capmt_size
= p_capmt_n
- p_capmt
;
1225 /*****************************************************************************
1227 *****************************************************************************/
1228 static void CAPMTFirst( access_t
* p_access
, int i_session_id
, uint8_t *p_pmt
)
1233 msg_Dbg( p_access
, "adding first CAPMT for SID %d on session %d",
1234 pmt_get_program( p_pmt
), i_session_id
);
1236 p_capmt
= CAPMTBuild( p_access
, i_session_id
, p_pmt
,
1237 0x3 /* only */, 0x1 /* ok_descrambling */,
1242 APDUSend( p_access
, i_session_id
, AOT_CA_PMT
, p_capmt
, i_capmt_size
);
1247 /*****************************************************************************
1249 *****************************************************************************/
1250 static void CAPMTAdd( access_t
* p_access
, int i_session_id
, uint8_t *p_pmt
)
1252 system_ids_t
*p_ids
=
1253 (system_ids_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1257 p_ids
->i_selected_programs
++;
1258 if( p_ids
->i_selected_programs
== 1 )
1260 CAPMTFirst( p_access
, i_session_id
, p_pmt
);
1264 msg_Dbg( p_access
, "adding CAPMT for SID %d on session %d",
1265 pmt_get_program( p_pmt
), i_session_id
);
1267 p_capmt
= CAPMTBuild( p_access
, i_session_id
, p_pmt
,
1268 0x4 /* add */, 0x1 /* ok_descrambling */,
1273 APDUSend( p_access
, i_session_id
, AOT_CA_PMT
, p_capmt
, i_capmt_size
);
1278 /*****************************************************************************
1280 *****************************************************************************/
1281 static void CAPMTUpdate( access_t
* p_access
, int i_session_id
, uint8_t *p_pmt
)
1286 msg_Dbg( p_access
, "updating CAPMT for SID %d on session %d",
1287 pmt_get_program( p_pmt
), i_session_id
);
1289 p_capmt
= CAPMTBuild( p_access
, i_session_id
, p_pmt
,
1290 0x5 /* update */, 0x1 /* ok_descrambling */,
1295 APDUSend( p_access
, i_session_id
, AOT_CA_PMT
, p_capmt
, i_capmt_size
);
1300 /*****************************************************************************
1302 *****************************************************************************/
1303 static void CAPMTDelete( access_t
* p_access
, int i_session_id
, uint8_t *p_pmt
)
1305 system_ids_t
*p_ids
=
1306 (system_ids_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1310 p_ids
->i_selected_programs
--;
1311 msg_Dbg( p_access
, "deleting CAPMT for SID %d on session %d",
1312 pmt_get_program( p_pmt
), i_session_id
);
1314 p_capmt
= CAPMTBuild( p_access
, i_session_id
, p_pmt
,
1315 0x5 /* update */, 0x4 /* not selected */,
1320 APDUSend( p_access
, i_session_id
, AOT_CA_PMT
, p_capmt
, i_capmt_size
);
1325 /*****************************************************************************
1326 * ConditionalAccessHandle
1327 *****************************************************************************/
1328 static void ConditionalAccessHandle( access_t
* p_access
, int i_session_id
,
1329 uint8_t *p_apdu
, int i_size
)
1331 system_ids_t
*p_ids
=
1332 (system_ids_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1333 int i_tag
= APDUGetTag( p_apdu
, i_size
);
1341 uint8_t *d
= APDUGetLength( p_apdu
, &l
);
1342 msg_Dbg( p_access
, "CA system IDs supported by the application :" );
1344 if ( p_ids
->i_nb_system_ids
)
1345 free( p_ids
->pi_system_ids
);
1346 p_ids
->i_nb_system_ids
= l
/ 2;
1347 p_ids
->pi_system_ids
= malloc( p_ids
->i_nb_system_ids
1348 * sizeof(uint16_t) );
1350 for ( i
= 0; i
< p_ids
->i_nb_system_ids
; i
++ )
1352 p_ids
->pi_system_ids
[i
] = ((uint16_t)d
[0] << 8) | d
[1];
1354 msg_Dbg( p_access
, "- 0x%x", p_ids
->pi_system_ids
[i
] );
1357 demux_ResendCAPMTs();
1362 /* http://www.cablelabs.com/specifications/OC-SP-HOSTPOD-IF-I08-011221.pdf */
1363 case AOT_CA_PMT_REPLY
:
1364 /* We do not care */
1369 "unexpected tag in ConditionalAccessHandle (0x%x)",
1374 /*****************************************************************************
1375 * ConditionalAccessClose
1376 *****************************************************************************/
1377 static void ConditionalAccessClose( access_t
* p_access
, int i_session_id
)
1380 msg_Dbg( p_access
, "closing ConditionalAccess session (%d)", i_session_id
);
1382 free( p_sessions
[i_session_id
- 1].p_sys
);
1385 /*****************************************************************************
1386 * ConditionalAccessOpen
1387 *****************************************************************************/
1388 static void ConditionalAccessOpen( access_t
* p_access
, int i_session_id
)
1391 msg_Dbg( p_access
, "opening ConditionalAccess session (%d)", i_session_id
);
1393 p_sessions
[i_session_id
- 1].pf_handle
= ConditionalAccessHandle
;
1394 p_sessions
[i_session_id
- 1].pf_close
= ConditionalAccessClose
;
1395 p_sessions
[i_session_id
- 1].p_sys
= malloc(sizeof(system_ids_t
));
1396 memset( p_sessions
[i_session_id
- 1].p_sys
, 0,
1397 sizeof(system_ids_t
) );
1399 APDUSend( p_access
, i_session_id
, AOT_CA_INFO_ENQ
, NULL
, 0 );
1412 /*****************************************************************************
1414 *****************************************************************************/
1415 static void DateTimeSend( access_t
* p_access
, int i_session_id
)
1417 date_time_t
*p_date
=
1418 (date_time_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1420 time_t t
= time(NULL
);
1424 if ( gmtime_r(&t
, &tm_gmt
) && localtime_r(&t
, &tm_loc
) )
1426 int Y
= tm_gmt
.tm_year
;
1427 int M
= tm_gmt
.tm_mon
+ 1;
1428 int D
= tm_gmt
.tm_mday
;
1429 int L
= (M
== 1 || M
== 2) ? 1 : 0;
1430 int MJD
= 14956 + D
+ (int)((Y
- L
) * 365.25)
1431 + (int)((M
+ 1 + L
* 12) * 30.6001);
1432 uint8_t p_response
[7];
1434 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
1436 p_response
[0] = htons(MJD
) >> 8;
1437 p_response
[1] = htons(MJD
) & 0xff;
1438 p_response
[2] = DEC2BCD(tm_gmt
.tm_hour
);
1439 p_response
[3] = DEC2BCD(tm_gmt
.tm_min
);
1440 p_response
[4] = DEC2BCD(tm_gmt
.tm_sec
);
1441 p_response
[5] = htons(tm_loc
.tm_gmtoff
/ 60) >> 8;
1442 p_response
[6] = htons(tm_loc
.tm_gmtoff
/ 60) & 0xff;
1444 APDUSend( p_access
, i_session_id
, AOT_DATE_TIME
, p_response
, 7 );
1446 p_date
->i_last
= i_wallclock
;
1450 /*****************************************************************************
1452 *****************************************************************************/
1453 static void DateTimeHandle( access_t
* p_access
, int i_session_id
,
1454 uint8_t *p_apdu
, int i_size
)
1456 date_time_t
*p_date
=
1457 (date_time_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1459 int i_tag
= APDUGetTag( p_apdu
, i_size
);
1463 case AOT_DATE_TIME_ENQ
:
1466 const uint8_t *d
= APDUGetLength( p_apdu
, &l
);
1470 p_date
->i_interval
= *d
;
1471 msg_Dbg( p_access
, "DateTimeHandle : interval set to %d",
1472 p_date
->i_interval
);
1475 p_date
->i_interval
= 0;
1477 DateTimeSend( p_access
, i_session_id
);
1481 msg_Err( p_access
, "unexpected tag in DateTimeHandle (0x%x)", i_tag
);
1485 /*****************************************************************************
1487 *****************************************************************************/
1488 static void DateTimeManage( access_t
* p_access
, int i_session_id
)
1490 date_time_t
*p_date
=
1491 (date_time_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1493 if ( p_date
->i_interval
1494 && i_wallclock
> p_date
->i_last
+ (mtime_t
)p_date
->i_interval
* 1000000 )
1496 DateTimeSend( p_access
, i_session_id
);
1500 /*****************************************************************************
1502 *****************************************************************************/
1503 static void DateTimeClose( access_t
* p_access
, int i_session_id
)
1505 msg_Dbg( p_access
, "closing DateTime session (%d)", i_session_id
);
1507 free( p_sessions
[i_session_id
- 1].p_sys
);
1510 /*****************************************************************************
1512 *****************************************************************************/
1513 static void DateTimeOpen( access_t
* p_access
, int i_session_id
)
1515 msg_Dbg( p_access
, "opening DateTime session (%d)", i_session_id
);
1517 p_sessions
[i_session_id
- 1].pf_handle
= DateTimeHandle
;
1518 p_sessions
[i_session_id
- 1].pf_manage
= DateTimeManage
;
1519 p_sessions
[i_session_id
- 1].pf_close
= DateTimeClose
;
1520 p_sessions
[i_session_id
- 1].p_sys
= malloc(sizeof(date_time_t
));
1521 memset( p_sessions
[i_session_id
- 1].p_sys
, 0, sizeof(date_time_t
) );
1523 DateTimeSend( p_access
, i_session_id
);
1530 /* Display Control Commands */
1532 #define DCC_SET_MMI_MODE 0x01
1533 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
1534 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
1535 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
1536 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
1540 #define MM_HIGH_LEVEL 0x01
1541 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
1542 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
1544 /* Display Reply IDs */
1546 #define DRI_MMI_MODE_ACK 0x01
1547 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
1548 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
1549 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
1550 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
1551 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
1552 #define DRI_UNKNOWN_MMI_MODE 0xF1
1553 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
1557 #define EF_BLIND 0x01
1561 #define AI_CANCEL 0x00
1562 #define AI_ANSWER 0x01
1566 en50221_mmi_object_t last_object
;
1569 static inline void en50221_MMIFree( en50221_mmi_object_t
*p_object
)
1573 switch ( p_object
->i_object_type
)
1575 case EN50221_MMI_ENQ
:
1576 free( p_object
->u
.enq
.psz_text
);
1579 case EN50221_MMI_ANSW
:
1580 if ( p_object
->u
.answ
.b_ok
)
1582 free( p_object
->u
.answ
.psz_answ
);
1586 case EN50221_MMI_MENU
:
1587 case EN50221_MMI_LIST
:
1588 free( p_object
->u
.menu
.psz_title
);
1589 free( p_object
->u
.menu
.psz_subtitle
);
1590 free( p_object
->u
.menu
.psz_bottom
);
1591 for ( i
= 0; i
< p_object
->u
.menu
.i_choices
; i
++ )
1593 free( p_object
->u
.menu
.ppsz_choices
[i
] );
1595 free( p_object
->u
.menu
.ppsz_choices
);
1603 /*****************************************************************************
1605 *****************************************************************************/
1606 static void MMISendObject( access_t
*p_access
, int i_session_id
,
1607 en50221_mmi_object_t
*p_object
)
1609 int i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
1613 switch ( p_object
->i_object_type
)
1615 case EN50221_MMI_ANSW
:
1617 i_size
= 1 + strlen( p_object
->u
.answ
.psz_answ
);
1618 p_data
= malloc( i_size
);
1619 p_data
[0] = (p_object
->u
.answ
.b_ok
== true) ? 0x1 : 0x0;
1620 strncpy( (char *)&p_data
[1], p_object
->u
.answ
.psz_answ
, i_size
- 1 );
1623 case EN50221_MMI_MENU_ANSW
:
1624 i_tag
= AOT_MENU_ANSW
;
1626 p_data
= malloc( i_size
);
1627 p_data
[0] = p_object
->u
.menu_answ
.i_choice
;
1631 msg_Err( p_access
, "unknown MMI object %d", p_object
->i_object_type
);
1635 APDUSend( p_access
, i_session_id
, i_tag
, p_data
, i_size
);
1638 p_slots
[i_slot
].b_mmi_expected
= true;
1641 /*****************************************************************************
1643 *****************************************************************************/
1644 static void MMISendClose( access_t
*p_access
, int i_session_id
)
1646 int i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
1648 APDUSend( p_access
, i_session_id
, AOT_CLOSE_MMI
, NULL
, 0 );
1650 p_slots
[i_slot
].b_mmi_expected
= true;
1653 /*****************************************************************************
1655 *****************************************************************************/
1656 static void MMIDisplayReply( access_t
*p_access
, int i_session_id
)
1658 uint8_t p_response
[2];
1660 p_response
[0] = DRI_MMI_MODE_ACK
;
1661 p_response
[1] = MM_HIGH_LEVEL
;
1663 APDUSend( p_access
, i_session_id
, AOT_DISPLAY_REPLY
, p_response
, 2 );
1665 msg_Dbg( p_access
, "sending DisplayReply on session (%d)", i_session_id
);
1668 /*****************************************************************************
1670 *****************************************************************************/
1671 static char *MMIGetText( access_t
*p_access
, uint8_t **pp_apdu
, int *pi_size
)
1673 int i_tag
= APDUGetTag( *pp_apdu
, *pi_size
);
1677 if ( i_tag
!= AOT_TEXT_LAST
)
1679 msg_Err( p_access
, "unexpected text tag: %06x", i_tag
);
1681 return strdup( "" );
1684 d
= APDUGetLength( *pp_apdu
, &l
);
1689 return dvb_string_get( d
, l
, demux_Iconv
, p_access
);
1692 /*****************************************************************************
1694 *****************************************************************************/
1695 static void MMIHandleEnq( access_t
*p_access
, int i_session_id
,
1696 uint8_t *p_apdu
, int i_size
)
1698 mmi_t
*p_mmi
= (mmi_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1699 int i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
1701 uint8_t *d
= APDUGetLength( p_apdu
, &l
);
1703 en50221_MMIFree( &p_mmi
->last_object
);
1704 p_mmi
->last_object
.i_object_type
= EN50221_MMI_ENQ
;
1705 p_mmi
->last_object
.u
.enq
.b_blind
= (*d
& 0x1) ? true : false;
1706 d
+= 2; /* skip answer_text_length because it is not mandatory */
1708 p_mmi
->last_object
.u
.enq
.psz_text
= malloc( l
+ 1 );
1709 strncpy( p_mmi
->last_object
.u
.enq
.psz_text
, (char *)d
, l
);
1710 p_mmi
->last_object
.u
.enq
.psz_text
[l
] = '\0';
1712 msg_Dbg( p_access
, "MMI enq: %s%s", p_mmi
->last_object
.u
.enq
.psz_text
,
1713 p_mmi
->last_object
.u
.enq
.b_blind
== true ? " (blind)" : "" );
1715 p_slots
[i_slot
].b_mmi_expected
= false;
1716 p_slots
[i_slot
].b_mmi_undisplayed
= true;
1719 /*****************************************************************************
1721 *****************************************************************************/
1722 static void MMIHandleMenu( access_t
*p_access
, int i_session_id
, int i_tag
,
1723 uint8_t *p_apdu
, int i_size
)
1725 mmi_t
*p_mmi
= (mmi_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1726 int i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
1728 uint8_t *d
= APDUGetLength( p_apdu
, &l
);
1730 en50221_MMIFree( &p_mmi
->last_object
);
1731 p_mmi
->last_object
.i_object_type
= (i_tag
== AOT_MENU_LAST
) ?
1732 EN50221_MMI_MENU
: EN50221_MMI_LIST
;
1733 p_mmi
->last_object
.u
.menu
.i_choices
= 0;
1734 p_mmi
->last_object
.u
.menu
.ppsz_choices
= NULL
;
1738 l
--; d
++; /* choice_nb */
1740 #define GET_FIELD( x ) \
1743 p_mmi->last_object.u.menu.psz_##x \
1744 = MMIGetText( p_access, &d, &l ); \
1745 msg_Dbg( p_access, "MMI " STRINGIFY( x ) ": %s", \
1746 p_mmi->last_object.u.menu.psz_##x ); \
1750 GET_FIELD( subtitle
);
1751 GET_FIELD( bottom
);
1756 char *psz_text
= MMIGetText( p_access
, &d
, &l
);
1757 TAB_APPEND( p_mmi
->last_object
.u
.menu
.i_choices
,
1758 p_mmi
->last_object
.u
.menu
.ppsz_choices
,
1760 msg_Dbg( p_access
, "MMI choice: %s", psz_text
);
1764 p_slots
[i_slot
].b_mmi_expected
= false;
1765 p_slots
[i_slot
].b_mmi_undisplayed
= true;
1768 /*****************************************************************************
1770 *****************************************************************************/
1771 static void MMIHandle( access_t
*p_access
, int i_session_id
,
1772 uint8_t *p_apdu
, int i_size
)
1774 int i_tag
= APDUGetTag( p_apdu
, i_size
);
1778 case AOT_DISPLAY_CONTROL
:
1781 uint8_t *d
= APDUGetLength( p_apdu
, &l
);
1787 case DCC_SET_MMI_MODE
:
1788 if ( l
== 2 && d
[1] == MM_HIGH_LEVEL
)
1789 MMIDisplayReply( p_access
, i_session_id
);
1791 msg_Err( p_access
, "unsupported MMI mode %02x", d
[1] );
1795 msg_Err( p_access
, "unsupported display control command %02x",
1804 MMIHandleEnq( p_access
, i_session_id
, p_apdu
, i_size
);
1809 MMIHandleMenu( p_access
, i_session_id
, i_tag
, p_apdu
, i_size
);
1813 SessionSendClose( p_access
, i_session_id
);
1817 msg_Err( p_access
, "unexpected tag in MMIHandle (0x%x)", i_tag
);
1821 /*****************************************************************************
1823 *****************************************************************************/
1824 static void MMIClose( access_t
*p_access
, int i_session_id
)
1826 int i_slot
= p_sessions
[i_session_id
- 1].i_slot
;
1827 mmi_t
*p_mmi
= (mmi_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1829 en50221_MMIFree( &p_mmi
->last_object
);
1830 free( p_sessions
[i_session_id
- 1].p_sys
);
1832 msg_Dbg( p_access
, "closing MMI session (%d)", i_session_id
);
1834 p_slots
[i_slot
].b_mmi_expected
= false;
1835 p_slots
[i_slot
].b_mmi_undisplayed
= true;
1838 /*****************************************************************************
1840 *****************************************************************************/
1841 static void MMIOpen( access_t
*p_access
, int i_session_id
)
1845 msg_Dbg( p_access
, "opening MMI session (%d)", i_session_id
);
1847 p_sessions
[i_session_id
- 1].pf_handle
= MMIHandle
;
1848 p_sessions
[i_session_id
- 1].pf_close
= MMIClose
;
1849 p_sessions
[i_session_id
- 1].p_sys
= malloc(sizeof(mmi_t
));
1850 p_mmi
= (mmi_t
*)p_sessions
[i_session_id
- 1].p_sys
;
1851 p_mmi
->last_object
.i_object_type
= EN50221_MMI_NONE
;
1859 /*****************************************************************************
1860 * InitSlot: Open the transport layer
1861 *****************************************************************************/
1862 static void InitSlot( access_t
* p_access
, int i_slot
)
1864 if ( TPDUSend( p_access
, i_slot
, T_CREATE_TC
, NULL
, 0 ) != 0 )
1865 msg_Err( p_access
, "en50221_Init: couldn't send TPDU on slot %d",
1869 /*****************************************************************************
1871 *****************************************************************************/
1872 static void ResetSlot( int i_slot
)
1874 ci_slot_t
*p_slot
= &p_slots
[i_slot
];
1877 if ( ioctl( i_ca_handle
, CA_RESET
, 1 << i_slot
) != 0 )
1878 msg_Err( NULL
, "en50221_Poll: couldn't reset slot %d", i_slot
);
1879 p_slot
->b_active
= false;
1880 p_slot
->i_init_timeout
= mdate() + CAM_INIT_TIMEOUT
;
1881 p_slot
->b_expect_answer
= false;
1882 p_slot
->b_mmi_expected
= false;
1883 p_slot
->b_mmi_undisplayed
= false;
1884 if ( p_slot
->p_recv
!= NULL
)
1886 free( p_slot
->p_recv
->p_data
);
1887 free( p_slot
->p_recv
);
1889 p_slot
->p_recv
= NULL
;
1890 while ( p_slot
->p_send
!= NULL
)
1892 en50221_msg_t
*p_next
= p_slot
->p_send
->p_next
;
1893 free( p_slot
->p_send
->p_data
);
1894 free( p_slot
->p_send
);
1895 p_slot
->p_send
= p_next
;
1897 p_slot
->pp_send_last
= &p_slot
->p_send
;
1899 /* Close all sessions for this slot. */
1900 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
1902 if ( p_sessions
[i_session_id
- 1].i_resource_id
1903 && p_sessions
[i_session_id
- 1].i_slot
== i_slot
)
1905 if ( p_sessions
[i_session_id
- 1].pf_close
!= NULL
)
1907 p_sessions
[i_session_id
- 1].pf_close( NULL
, i_session_id
);
1909 p_sessions
[i_session_id
- 1].i_resource_id
= 0;
1916 * External entry points
1919 /*****************************************************************************
1920 * en50221_Init : Initialize the CAM for en50221
1921 *****************************************************************************/
1922 void en50221_Init( void )
1927 memset( &caps
, 0, sizeof( ca_caps_t
));
1929 sprintf( psz_tmp
, "/dev/dvb/adapter%d/ca0", i_adapter
);
1930 if( (i_ca_handle
= open(psz_tmp
, O_RDWR
| O_NONBLOCK
)) < 0 )
1932 msg_Warn( NULL
, "failed opening CAM device %s (%s)",
1933 psz_tmp
, strerror(errno
) );
1938 if ( ioctl( i_ca_handle
, CA_GET_CAP
, &caps
) != 0 )
1940 msg_Err( NULL
, "failed getting CAM capabilities (%s)",
1942 close( i_ca_handle
);
1947 /* Output CA capabilities */
1948 msg_Dbg( NULL
, "CA interface with %d %s", caps
.slot_num
,
1949 caps
.slot_num
== 1 ? "slot" : "slots" );
1950 if ( caps
.slot_type
& CA_CI
)
1951 msg_Dbg( NULL
, " CI high level interface type" );
1952 if ( caps
.slot_type
& CA_CI_LINK
)
1953 msg_Dbg( NULL
, " CI link layer level interface type" );
1954 if ( caps
.slot_type
& CA_CI_PHYS
)
1955 msg_Dbg( NULL
, " CI physical layer level interface type (not supported) " );
1956 if ( caps
.slot_type
& CA_DESCR
)
1957 msg_Dbg( NULL
, " built-in descrambler detected" );
1958 if ( caps
.slot_type
& CA_SC
)
1959 msg_Dbg( NULL
, " simple smart card interface" );
1961 msg_Dbg( NULL
, " %d available %s", caps
.descr_num
,
1962 caps
.descr_num
== 1 ? "descrambler (key)" : "descramblers (keys)" );
1963 if ( caps
.descr_type
& CA_ECD
)
1964 msg_Dbg( NULL
, " ECD scrambling system supported" );
1965 if ( caps
.descr_type
& CA_NDS
)
1966 msg_Dbg( NULL
, " NDS scrambling system supported" );
1967 if ( caps
.descr_type
& CA_DSS
)
1968 msg_Dbg( NULL
, " DSS scrambling system supported" );
1970 if ( caps
.slot_num
== 0 )
1972 msg_Err( NULL
, "CAM module with no slots" );
1973 close( i_ca_handle
);
1978 if( caps
.slot_type
& CA_CI_LINK
)
1979 i_ca_type
= CA_CI_LINK
;
1980 else if( caps
.slot_type
& CA_CI
)
1984 msg_Err( NULL
, "Incompatible CAM interface" );
1985 close( i_ca_handle
);
1990 i_nb_slots
= caps
.slot_num
;
1991 memset( p_sessions
, 0, sizeof(en50221_session_t
) * MAX_SESSIONS
);
1996 /*****************************************************************************
1997 * en50221_Reset : Reset the CAM for en50221
1998 *****************************************************************************/
1999 void en50221_Reset( void )
2001 switch (i_print_type
)
2004 printf("<STATUS type=\"cam\" status=\"0\" />\n");
2010 memset( p_slots
, 0, sizeof(ci_slot_t
) * MAX_CI_SLOTS
);
2012 if( i_ca_type
& CA_CI_LINK
)
2015 for ( i_slot
= 0; i_slot
< i_nb_slots
; i_slot
++ )
2016 ResetSlot( i_slot
);
2020 struct ca_slot_info info
;
2021 system_ids_t
*p_ids
;
2025 /* We don't reset the CAM in that case because it's done by the
2027 if ( ioctl( i_ca_handle
, CA_GET_SLOT_INFO
, &info
) < 0 )
2029 msg_Err( NULL
, "en50221_Init: couldn't get slot info" );
2030 close( i_ca_handle
);
2034 if( info
.flags
== 0 )
2036 msg_Err( NULL
, "en50221_Init: no CAM inserted" );
2037 close( i_ca_handle
);
2042 /* Allocate a dummy sessions */
2043 p_sessions
[0].i_resource_id
= RI_CONDITIONAL_ACCESS_SUPPORT
;
2044 p_sessions
[0].pf_close
= ConditionalAccessClose
;
2045 if ( p_sessions
[0].p_sys
== NULL
)
2046 p_sessions
[0].p_sys
= malloc(sizeof(system_ids_t
));
2047 memset( p_sessions
[0].p_sys
, 0, sizeof(system_ids_t
) );
2048 p_ids
= (system_ids_t
*)p_sessions
[0].p_sys
;
2049 p_ids
->b_high_level
= 1;
2051 /* Get application info to find out which cam we are using and make
2052 sure everything is ready to play */
2054 ca_msg
.msg
[0] = ( AOT_APPLICATION_INFO
& 0xFF0000 ) >> 16;
2055 ca_msg
.msg
[1] = ( AOT_APPLICATION_INFO
& 0x00FF00 ) >> 8;
2056 ca_msg
.msg
[2] = ( AOT_APPLICATION_INFO
& 0x0000FF ) >> 0;
2057 memset( &ca_msg
.msg
[3], 0, 253 );
2058 APDUSend( NULL
, 1, AOT_APPLICATION_INFO_ENQ
, NULL
, 0 );
2059 if ( ioctl( i_ca_handle
, CA_GET_MSG
, &ca_msg
) < 0 )
2061 msg_Err( NULL
, "en50221_Init: failed getting message" );
2062 close( i_ca_handle
);
2067 #ifdef HLCI_WAIT_CAM_READY
2068 while( ca_msg
.msg
[8] == 0xff && ca_msg
.msg
[9] == 0xff )
2071 msg_Dbg( NULL
, "CAM: please wait" );
2072 APDUSend( NULL
, 1, AOT_APPLICATION_INFO_ENQ
, NULL
, 0 );
2074 ca_msg
.msg
[0] = ( AOT_APPLICATION_INFO
& 0xFF0000 ) >> 16;
2075 ca_msg
.msg
[1] = ( AOT_APPLICATION_INFO
& 0x00FF00 ) >> 8;
2076 ca_msg
.msg
[2] = ( AOT_APPLICATION_INFO
& 0x0000FF ) >> 0;
2077 memset( &ca_msg
.msg
[3], 0, 253 );
2078 if ( ioctl( i_ca_handle
, CA_GET_MSG
, &ca_msg
) < 0 )
2080 msg_Err( NULL
, "en50221_Init: failed getting message" );
2081 close( i_ca_handle
);
2085 msg_Dbg( NULL
, "en50221_Init: Got length: %d, tag: 0x%x", ca_msg
.length
, APDUGetTag( ca_msg
.msg
, ca_msg
.length
) );
2088 if( ca_msg
.msg
[8] == 0xff && ca_msg
.msg
[9] == 0xff )
2090 msg_Err( NULL
, "CAM returns garbage as application info!" );
2091 close( i_ca_handle
);
2096 msg_Dbg( NULL
, "found CAM %s using id 0x%x", &ca_msg
.msg
[12],
2097 (ca_msg
.msg
[8]<<8)|ca_msg
.msg
[9] );
2101 /*****************************************************************************
2102 * en50221_Read : Read the CAM for a TPDU
2103 *****************************************************************************/
2104 void en50221_Read( void )
2109 /*****************************************************************************
2110 * en50221_Poll : Send a poll TPDU to the CAM
2111 *****************************************************************************/
2112 void en50221_Poll( void )
2117 /* Check module status */
2118 for ( i_slot
= 0; i_slot
< i_nb_slots
; i_slot
++ )
2120 ci_slot_t
*p_slot
= &p_slots
[i_slot
];
2121 ca_slot_info_t sinfo
;
2124 if ( ioctl( i_ca_handle
, CA_GET_SLOT_INFO
, &sinfo
) != 0 )
2126 msg_Err( NULL
, "en50221_Poll: couldn't get info on slot %d",
2131 if ( !(sinfo
.flags
& CA_CI_MODULE_READY
) )
2133 if ( p_slot
->b_active
)
2135 msg_Dbg( NULL
, "en50221_Poll: slot %d has been removed",
2137 ResetSlot( i_slot
);
2142 else if ( !p_slot
->b_active
)
2144 if ( !p_slot
->b_expect_answer
)
2145 InitSlot( NULL
, i_slot
);
2146 else if ( p_slot
->i_init_timeout
< i_wallclock
)
2148 msg_Dbg( NULL
, "en50221_Poll: resetting slot %d", i_slot
);
2149 ResetSlot( i_slot
);
2155 /* Check if applications have data to send */
2156 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2158 en50221_session_t
*p_session
= &p_sessions
[i_session_id
- 1];
2159 if ( p_session
->i_resource_id
&& p_session
->pf_manage
!= NULL
2160 && !p_slots
[ p_session
->i_slot
].b_expect_answer
)
2161 p_session
->pf_manage( NULL
, i_session_id
);
2164 /* Now send the poll command to inactive slots */
2165 for ( i_slot
= 0; i_slot
< i_nb_slots
; i_slot
++ )
2167 ci_slot_t
*p_slot
= &p_slots
[i_slot
];
2169 if ( p_slot
->b_active
&& !p_slot
->b_expect_answer
)
2171 if ( TPDUSend( NULL
, i_slot
, T_DATA_LAST
, NULL
, 0 ) != 0 )
2174 "en50221_Poll: couldn't send TPDU on slot %d, resetting",
2176 ResetSlot( i_slot
);
2182 /*****************************************************************************
2184 *****************************************************************************/
2185 void en50221_AddPMT( uint8_t *p_pmt
)
2189 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2190 if ( p_sessions
[i_session_id
- 1].i_resource_id
2191 == RI_CONDITIONAL_ACCESS_SUPPORT
)
2192 CAPMTAdd( NULL
, i_session_id
, p_pmt
);
2195 /*****************************************************************************
2196 * en50221_UpdatePMT :
2197 *****************************************************************************/
2198 void en50221_UpdatePMT( uint8_t *p_pmt
)
2202 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2203 if ( p_sessions
[i_session_id
- 1].i_resource_id
2204 == RI_CONDITIONAL_ACCESS_SUPPORT
)
2205 CAPMTUpdate( NULL
, i_session_id
, p_pmt
);
2208 /*****************************************************************************
2209 * en50221_DeletePMT :
2210 *****************************************************************************/
2211 void en50221_DeletePMT( uint8_t *p_pmt
)
2215 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2216 if ( p_sessions
[i_session_id
- 1].i_resource_id
2217 == RI_CONDITIONAL_ACCESS_SUPPORT
)
2218 CAPMTDelete( NULL
, i_session_id
, p_pmt
);
2221 /*****************************************************************************
2222 * en50221_StatusMMI :
2223 *****************************************************************************/
2224 uint8_t en50221_StatusMMI( uint8_t *p_answer
, ssize_t
*pi_size
)
2226 struct ret_mmi_status
*p_ret
= (struct ret_mmi_status
*)p_answer
;
2228 if ( ioctl( i_ca_handle
, CA_GET_CAP
, &p_ret
->caps
) != 0 )
2230 msg_Err( NULL
, "ioctl CA_GET_CAP failed (%s)", strerror(errno
) );
2234 *pi_size
= sizeof(struct ret_mmi_status
);
2235 return RET_MMI_STATUS
;
2238 /*****************************************************************************
2239 * en50221_StatusMMISlot :
2240 *****************************************************************************/
2241 uint8_t en50221_StatusMMISlot( uint8_t *p_buffer
, ssize_t i_size
,
2242 uint8_t *p_answer
, ssize_t
*pi_size
)
2245 struct ret_mmi_slot_status
*p_ret
= (struct ret_mmi_slot_status
*)p_answer
;
2247 if ( i_size
!= 1 ) return RET_HUH
;
2250 p_ret
->sinfo
.num
= i_slot
;
2251 if ( ioctl( i_ca_handle
, CA_GET_SLOT_INFO
, &p_ret
->sinfo
) != 0 )
2253 msg_Err( NULL
, "ioctl CA_GET_SLOT_INFO failed (%s)", strerror(errno
) );
2257 *pi_size
= sizeof(struct ret_mmi_slot_status
);
2258 return RET_MMI_SLOT_STATUS
;
2261 /*****************************************************************************
2263 *****************************************************************************/
2264 uint8_t en50221_OpenMMI( uint8_t *p_buffer
, ssize_t i_size
)
2268 if ( i_size
!= 1 ) return RET_HUH
;
2271 if( i_ca_type
& CA_CI_LINK
)
2274 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2276 if ( p_sessions
[i_session_id
- 1].i_resource_id
== RI_MMI
2277 && p_sessions
[i_session_id
- 1].i_slot
== i_slot
)
2280 "MMI menu is already opened on slot %d (session=%d)",
2281 i_slot
, i_session_id
);
2286 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2288 if ( p_sessions
[i_session_id
- 1].i_resource_id
2289 == RI_APPLICATION_INFORMATION
2290 && p_sessions
[i_session_id
- 1].i_slot
== i_slot
)
2292 ApplicationInformationEnterMenu( NULL
, i_session_id
);
2297 msg_Err( NULL
, "no application information on slot %d", i_slot
);
2302 msg_Err( NULL
, "MMI menu not supported" );
2307 /*****************************************************************************
2308 * en50221_CloseMMI :
2309 *****************************************************************************/
2310 uint8_t en50221_CloseMMI( uint8_t *p_buffer
, ssize_t i_size
)
2314 if ( i_size
!= 1 ) return RET_HUH
;
2317 if( i_ca_type
& CA_CI_LINK
)
2320 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2322 if ( p_sessions
[i_session_id
- 1].i_resource_id
== RI_MMI
2323 && p_sessions
[i_session_id
- 1].i_slot
== i_slot
)
2325 MMISendClose( NULL
, i_session_id
);
2330 msg_Warn( NULL
, "closing a non-existing MMI session on slot %d",
2336 msg_Err( NULL
, "MMI menu not supported" );
2341 /*****************************************************************************
2342 * en50221_GetMMIObject :
2343 *****************************************************************************/
2344 uint8_t en50221_GetMMIObject( uint8_t *p_buffer
, ssize_t i_size
,
2345 uint8_t *p_answer
, ssize_t
*pi_size
)
2347 int i_session_id
, i_slot
;
2348 struct ret_mmi_recv
*p_ret
= (struct ret_mmi_recv
*)p_answer
;
2350 if ( i_size
!= 1 ) return RET_HUH
;
2353 if ( p_slots
[i_slot
].b_mmi_expected
)
2354 return RET_MMI_WAIT
; /* data not yet available */
2356 p_ret
->object
.i_object_type
= EN50221_MMI_NONE
;
2357 *pi_size
= sizeof(struct ret_mmi_recv
);
2359 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2361 if ( p_sessions
[i_session_id
- 1].i_resource_id
== RI_MMI
2362 && p_sessions
[i_session_id
- 1].i_slot
== i_slot
)
2365 (mmi_t
*)p_sessions
[i_session_id
- 1].p_sys
;
2366 if ( p_mmi
== NULL
)
2369 return RET_ERR
; /* should not happen */
2372 *pi_size
= COMM_BUFFER_SIZE
- COMM_HEADER_SIZE
-
2373 ((void *)&p_ret
->object
- (void *)p_ret
);
2374 if ( en50221_SerializeMMIObject( (uint8_t *)&p_ret
->object
,
2375 pi_size
, &p_mmi
->last_object
) == -1 )
2378 msg_Err( NULL
, "MMI structure too big" );
2381 *pi_size
+= ((void *)&p_ret
->object
- (void *)p_ret
);
2386 return RET_MMI_RECV
;
2390 /*****************************************************************************
2391 * en50221_SendMMIObject :
2392 *****************************************************************************/
2393 uint8_t en50221_SendMMIObject( uint8_t *p_buffer
, ssize_t i_size
)
2395 int i_session_id
, i_slot
;
2396 struct cmd_mmi_send
*p_cmd
= (struct cmd_mmi_send
*)p_buffer
;
2398 if ( en50221_UnserializeMMIObject( &p_cmd
->object
, i_size
-
2399 ((void *)&p_cmd
->object
- (void *)p_cmd
) ) == -1 )
2402 i_slot
= p_cmd
->i_slot
;
2404 for ( i_session_id
= 1; i_session_id
<= MAX_SESSIONS
; i_session_id
++ )
2406 if ( p_sessions
[i_session_id
- 1].i_resource_id
== RI_MMI
2407 && p_sessions
[i_session_id
- 1].i_slot
== i_slot
)
2409 MMISendObject( NULL
, i_session_id
, &p_cmd
->object
);
2414 msg_Err( NULL
, "SendMMIObject when no MMI session is opened !" );