* dvbiscovery.sh: NIT can take longer than 15s. * dvbiscovery_dvb-s.conf: Change...
[dvblast.git] / en50221.c
blobf5e9a2dcfdc80900862e371e09a5db870413d82a
1 /*****************************************************************************
2 * en50221.c : implementation of the transport, session and applications
3 * layers of EN 50 221
4 *****************************************************************************
5 * Copyright (C) 2004-2005, 2010 VideoLAN
6 * $Id$
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 *****************************************************************************/
26 #include <stdlib.h>
27 #include <stdint.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <inttypes.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <sys/ioctl.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <poll.h>
41 #include <errno.h>
42 #include <time.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>
54 #include "dvblast.h"
55 #include "en50221.h"
56 #include "comm.h"
58 #define TAB_APPEND( count, tab, p ) \
59 if( (count) > 0 ) \
60 { \
61 (tab) = realloc( tab, sizeof( void ** ) * ( (count) + 1 ) ); \
62 } \
63 else \
64 { \
65 (tab) = malloc( sizeof( void ** ) ); \
66 } \
67 (tab)[count] = (p); \
68 (count)++
70 /*****************************************************************************
71 * Local declarations
72 *****************************************************************************/
73 #undef DEBUG_TPDU
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
80 uint8_t *p_data;
81 int i_size;
82 struct en50221_msg_t *p_next;
83 } en50221_msg_t;
85 typedef struct en50221_session_t
87 int i_slot;
88 int i_resource_id;
89 void (* pf_handle)( access_t *, int, uint8_t *, int );
90 void (* pf_close)( access_t *, int );
91 void (* pf_manage)( access_t *, int );
92 void *p_sys;
93 } en50221_session_t;
95 typedef struct ci_slot_t
97 bool b_active;
98 bool b_expect_answer;
99 bool b_has_data;
100 bool b_mmi_expected;
101 bool b_mmi_undisplayed;
103 /* TPDU reception */
104 en50221_msg_t *p_recv;
106 /* TPDU emission */
107 en50221_msg_t *p_send;
108 en50221_msg_t **pp_send_last;
109 uint8_t *p;
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;
117 } ci_slot_t;
119 int i_ca_handle = 0;
120 int i_ca_type = -1;
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 /*****************************************************************************
127 * Local prototypes
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 /*****************************************************************************
140 * Utility functions
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;
151 int i;
153 *pi_length = 0;
154 for ( i = 0; i < l; i++ )
155 *pi_length = (*pi_length << 8) | *p_data++;
158 return p_data;
161 static uint8_t *SetLength( uint8_t *p_data, int i_length )
163 uint8_t *p = p_data;
165 if ( i_length < 128 )
167 *p++ = i_length;
169 else if ( i_length < 256 )
171 *p++ = SIZE_INDICATOR | 0x1;
172 *p++ = i_length;
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;
187 else
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;
196 return p;
201 * Transport layer
204 #define MAX_TPDU_SIZE 4096
205 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
207 #define DATA_INDICATOR 0x80
209 #define T_SB 0x80
210 #define T_RCV 0x81
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 )
223 #ifdef DEBUG_TPDU
224 int i;
225 #define MAX_DUMP 256
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 ? "..." : "");
230 #endif
233 /*****************************************************************************
234 * TPDUWrite
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 )
242 msg_Warn( p_access,
243 "en50221: writing while expecting an answer on slot %u",
244 i_slot );
245 if ( p_send == NULL )
247 msg_Warn( p_access, "en50221: no data to write on slot %u !", i_slot );
248 return -1;
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 )
257 != p_send->i_size )
259 msg_Err( p_access, "en50221: cannot write to CAM device (%m)" );
260 free( p_send->p_data );
261 free( p_send );
262 return -1;
265 free( p_send->p_data );
266 free( p_send );
267 p_slot->b_expect_answer = true;
269 return 0;
272 /*****************************************************************************
273 * TPDUSend
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 );
282 int i_size;
284 i_size = 0;
285 p_data[0] = i_slot;
286 p_data[1] = i_tcid;
287 p_data[2] = i_tag;
289 switch ( i_tag )
291 case T_RCV:
292 case T_CREATE_TC:
293 case T_CTC_REPLY:
294 case T_DELETE_TC:
295 case T_DTC_REPLY:
296 case T_REQUEST_TC:
297 p_data[3] = 1; /* length */
298 p_data[4] = i_tcid;
299 i_size = 5;
300 break;
302 case T_NEW_TC:
303 case T_TC_ERROR:
304 p_data[3] = 2; /* length */
305 p_data[4] = i_tcid;
306 p_data[5] = p_content[0];
307 i_size = 6;
308 break;
310 case T_DATA_LAST:
311 case T_DATA_MORE:
313 /* i_length <= MAX_TPDU_DATA */
314 uint8_t *p = p_data + 3;
315 p = SetLength( p, i_length + 1 );
316 *p++ = i_tcid;
318 if ( i_length )
319 memcpy( p, p_content, i_length );
320 i_size = i_length + (p - p_data);
322 break;
324 default:
325 break;
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 );
338 return 0;
342 /*****************************************************************************
343 * TPDURecv
344 *****************************************************************************/
345 static int TPDURecv( access_t * p_access )
347 ci_slot_t *p_slot;
348 uint8_t i_tag, i_slot;
349 uint8_t p_data[MAX_TPDU_SIZE];
350 int i_size;
351 bool b_last = false;
355 i_size = read( i_ca_handle, p_data, MAX_TPDU_SIZE );
357 while ( i_size < 0 && errno == EINTR );
359 if ( i_size < 5 )
361 msg_Err( p_access, "en50221: cannot read from CAM device (%d:%m)",
362 i_size );
363 return -1;
366 Dump( false, p_data, i_size );
368 i_slot = p_data[1] - 1;
369 i_tag = p_data[2];
371 if ( i_slot >= i_nb_slots )
373 msg_Warn( p_access, "en50221: TPDU is from an unknown slot %u",
374 i_slot );
375 return -1;
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;
384 switch ( i_tag )
386 case T_CTC_REPLY:
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 );
390 break;
392 case T_SB:
393 break;
395 case T_DATA_LAST:
396 b_last = true;
397 /* intended pass-through */
398 case T_DATA_MORE:
400 en50221_msg_t *p_recv;
401 int i_session_size;
402 uint8_t *p_session = GetLength( &p_data[3], &i_session_size );
404 if ( i_session_size <= 1 )
405 break;
406 p_session++;
407 i_session_size--;
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;
422 if ( b_last )
424 SPDUHandle( p_access, i_slot, p_recv->p_data, p_recv->i_size );
425 free( p_recv->p_data );
426 free( p_recv );
427 p_slot->p_recv = NULL;
429 break;
432 default:
433 msg_Warn( p_access, "en50221: unhandled R_TPDU tag %u slot %u", i_tag,
434 i_slot );
435 break;
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 );
445 return 0;
450 * Session layer
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
461 #define SS_OK 0x00
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 /*****************************************************************************
478 * SPDUSend
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 );
484 uint8_t *p = p_spdu;
485 uint8_t i_slot = p_sessions[i_session_id - 1].i_slot;
487 *p++ = ST_SESSION_NUMBER;
488 *p++ = 0x02;
489 *p++ = (i_session_id >> 8);
490 *p++ = i_session_id & 0xff;
492 memcpy( p, p_data, i_size );
494 i_size += 4;
495 p = p_spdu;
497 while ( i_size > 0 )
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",
505 i_session_id );
506 free( p_spdu );
507 return -1;
509 p += MAX_TPDU_DATA;
510 i_size -= MAX_TPDU_DATA;
512 else
514 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
515 != 0 )
517 msg_Err( p_access, "couldn't send TPDU on session %d",
518 i_session_id );
519 free( p_spdu );
520 return -1;
522 i_size = 0;
526 free( p_spdu );
527 return 0;
530 /*****************************************************************************
531 * SessionOpen
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;
549 case RI_DATE_TIME:
550 DateTimeOpen( p_access, i_session_id ); break;
551 case RI_MMI:
552 MMIOpen( p_access, i_session_id ); break;
554 case RI_HOST_CONTROL:
555 default:
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];
565 int i_session_id;
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 )
573 break;
575 if ( i_session_id == MAX_SESSIONS )
577 msg_Err( p_access, "too many sessions !" );
578 return;
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 )
591 i_status = SS_OK;
594 p_response[0] = ST_OPEN_SESSION_RESPONSE;
595 p_response[1] = 0x7;
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 )
606 msg_Err( p_access,
607 "SessionOpen: couldn't send TPDU on slot %d", i_slot );
608 return;
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;
617 #if 0
618 /* unused code for the moment - commented out to keep gcc happy */
619 /*****************************************************************************
620 * SessionCreate
621 *****************************************************************************/
622 static void SessionCreate( access_t * p_access, int i_slot, int i_resource_id )
624 uint8_t p_response[16];
625 uint8_t i_tag;
626 int i_session_id;
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 )
631 break;
633 if ( i_session_id == MAX_SESSIONS )
635 msg_Err( p_access, "too many sessions !" );
636 return;
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;
645 p_response[1] = 0x6;
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 ) !=
656 msg_Err( p_access,
657 "SessionCreate: couldn't send TPDU on slot %d", i_slot );
658 return;
661 #endif
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,
677 i_status );
678 p_sessions[i_session_id - 1].i_resource_id = 0;
679 return;
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;
690 case RI_DATE_TIME:
691 DateTimeOpen( p_access, i_session_id ); break;
692 case RI_MMI:
693 MMIOpen( p_access, i_session_id ); break;
695 case RI_HOST_CONTROL:
696 default:
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 /*****************************************************************************
703 * SessionSendClose
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;
711 p_response[1] = 0x2;
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 ) !=
718 msg_Err( p_access,
719 "SessionSendClose: couldn't send TPDU on slot %d", i_slot );
720 return;
724 /*****************************************************************************
725 * SessionClose
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;
737 p_response[1] = 0x3;
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 ) !=
745 msg_Err( p_access,
746 "SessionClose: couldn't send TPDU on slot %d", i_slot );
747 return;
751 /*****************************************************************************
752 * SPDUHandle
753 *****************************************************************************/
754 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
755 uint8_t *p_spdu, int i_size )
757 int i_session_id;
759 switch ( p_spdu[0] )
761 case ST_SESSION_NUMBER:
762 if ( i_size <= 4 )
763 return;
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 );
767 break;
769 case ST_OPEN_SESSION_REQUEST:
770 if ( i_size != 6 || p_spdu[1] != 0x4 )
771 return;
772 SessionOpen( p_access, i_slot, p_spdu, i_size );
773 break;
775 case ST_CREATE_SESSION_RESPONSE:
776 if ( i_size != 9 || p_spdu[1] != 0x7 )
777 return;
778 SessionCreateResponse( p_access, i_slot, p_spdu, i_size );
779 break;
781 case ST_CLOSE_SESSION_REQUEST:
782 if ( i_size != 4 || p_spdu[1] != 0x2 )
783 return;
784 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
785 SessionClose( p_access, i_session_id );
786 break;
788 case ST_CLOSE_SESSION_RESPONSE:
789 if ( i_size != 5 || p_spdu[1] != 0x3 )
790 return;
791 i_session_id = ((int)p_spdu[3] << 8) | p_spdu[4];
792 if ( p_spdu[2] )
794 msg_Err( p_access, "closing a session which is not allocated (%d)",
795 i_session_id );
797 else
799 if ( p_sessions[i_session_id - 1].pf_close != NULL )
800 p_sessions[i_session_id - 1].pf_close( p_access,
801 i_session_id );
802 p_sessions[i_session_id - 1].i_resource_id = 0;
804 break;
806 default:
807 msg_Err( p_access, "unexpected tag in SPDUHandle (%x)", p_spdu[0] );
808 break;
814 * Application layer
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 /*****************************************************************************
868 * APDUGetTag
869 *****************************************************************************/
870 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
872 if ( i_size >= 3 )
874 int i, t = 0;
875 for ( i = 0; i < 3; i++ )
876 t = (t << 8) | *p_apdu++;
877 return t;
880 return AOT_NONE;
883 /*****************************************************************************
884 * APDUGetLength
885 *****************************************************************************/
886 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
888 return GetLength( &p_apdu[3], pi_size );
891 /*****************************************************************************
892 * APDUSend
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 );
898 uint8_t *p = p_apdu;
899 ca_msg_t ca_msg;
900 int i_ret;
902 *p++ = (i_tag >> 16);
903 *p++ = (i_tag >> 8) & 0xff;
904 *p++ = i_tag & 0xff;
905 p = SetLength( p, i_size );
906 if ( 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 );
912 else
914 if ( i_size + p - p_apdu > 256 )
916 msg_Err( p_access, "CAM: apdu overflow" );
917 i_ret = -1;
919 else
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 );
925 if ( i_ret < 0 )
927 msg_Err( p_access, "Error sending to CAM: %m" );
928 i_ret = -1;
932 free( p_apdu );
933 return i_ret;
937 * Resource Manager
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 );
948 switch ( i_tag )
950 case AOT_PROFILE_ENQ:
952 int resources[] = { htonl(RI_RESOURCE_MANAGER),
953 htonl(RI_APPLICATION_INFORMATION),
954 htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
955 htonl(RI_DATE_TIME),
956 htonl(RI_MMI)
958 APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
959 sizeof(resources) );
960 break;
962 case AOT_PROFILE:
963 APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
964 break;
966 default:
967 msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
968 i_tag );
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,
993 int i_session_id )
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 );
1010 switch ( i_tag )
1012 case AOT_APPLICATION_INFO:
1014 int i_type, i_manufacturer, i_code;
1015 int l = 0;
1016 uint8_t *d = APDUGetLength( p_apdu, &l );
1018 if ( l < 4 ) break;
1020 i_type = *d++;
1021 i_manufacturer = ((int)d[0] << 8) | d[1];
1022 d += 2;
1023 i_code = ((int)d[0] << 8) | d[1];
1024 d += 2;
1025 d = GetLength( d, &l );
1028 char *psz_name = malloc(l + 1);
1029 memcpy( psz_name, d, l );
1030 psz_name[l] = '\0';
1031 msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
1032 psz_name, i_type, i_manufacturer, i_code );
1033 switch (i_print_type)
1035 case PRINT_XML:
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);
1039 break;
1040 default:
1041 printf("CAM: name=%s type=%d manufacturer=%d product=%d\n",
1042 psz_name, i_type, i_manufacturer, i_code);
1044 free(psz_name);
1046 break;
1048 default:
1049 msg_Err( p_access,
1050 "unexpected tag in ApplicationInformationHandle (0x%x)",
1051 i_tag );
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
1072 typedef struct
1074 int i_nb_system_ids;
1075 uint16_t *pi_system_ids;
1077 int i_selected_programs;
1078 int b_high_level;
1079 } system_ids_t;
1081 static bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
1083 int i;
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 )
1089 return true;
1091 return false;
1094 /*****************************************************************************
1095 * CAPMTBuild
1096 *****************************************************************************/
1097 static bool HasCADescriptors( system_ids_t *p_ids, uint8_t *p_descs )
1099 const uint8_t *p_desc;
1100 uint16_t j = 0;
1102 while ( (p_desc = descs_get_desc( p_descs, j )) != NULL )
1104 uint8_t i_tag = desc_get_tag( p_desc );
1105 j++;
1107 if ( i_tag == 0x9 && desc09_validate( p_desc )
1108 && CheckSystemID( p_ids, desc09_get_sysid( p_desc ) ) )
1109 return true;
1112 return false;
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 );
1128 j++;
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 );
1134 k++;
1135 memcpy( p_info, p_desc,
1136 DESC_HEADER_SIZE + desc_get_length( p_desc ) );
1140 if ( k )
1142 uint8_t *p_info = capmti_get_info( p_infos, k );
1143 capmti_set_length( p_infos, p_info - p_infos - DESCS_HEADER_SIZE );
1145 else
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;
1155 uint8_t *p_es;
1156 uint8_t *p_capmt, *p_capmt_n;
1157 uint16_t j, k;
1158 bool b_has_ca = HasCADescriptors( p_ids, pmt_get_descs( p_pmt ) );
1159 bool b_has_es = false;
1161 j = 0;
1162 while ( (p_es = pmt_get_es( p_pmt, j )) != NULL )
1164 uint16_t i_pid = pmtn_get_pid( p_es );
1165 j++;
1167 if ( demux_PIDIsSelected( i_pid ) )
1169 b_has_es = true;
1170 b_has_ca = b_has_ca
1171 || HasCADescriptors( p_ids, pmtn_get_descs( p_es ) );
1175 if ( !b_has_es )
1177 *pi_capmt_size = 0;
1178 return NULL;
1181 if ( !b_has_ca )
1183 msg_Warn( p_access,
1184 "no compatible scrambling system for SID %d on session %d",
1185 pmt_get_program( p_pmt ), i_session_id );
1186 *pi_capmt_size = 0;
1187 return NULL;
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 ) );
1199 j = 0; k = 0;
1200 while ( (p_es = pmt_get_es( p_pmt, j )) != NULL )
1202 uint16_t i_pid = pmtn_get_pid( p_es );
1203 j++;
1205 if ( !demux_PIDIsSelected( i_pid ) )
1206 continue;
1208 p_capmt_n = capmt_get_es( p_capmt, k );
1209 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;
1222 return p_capmt;
1225 /*****************************************************************************
1226 * CAPMTFirst
1227 *****************************************************************************/
1228 static void CAPMTFirst( access_t * p_access, int i_session_id, uint8_t *p_pmt )
1230 uint8_t *p_capmt;
1231 int i_capmt_size;
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 */,
1238 &i_capmt_size );
1240 if ( i_capmt_size )
1242 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1243 free( p_capmt );
1247 /*****************************************************************************
1248 * CAPMTAdd
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;
1254 uint8_t *p_capmt;
1255 int i_capmt_size;
1257 p_ids->i_selected_programs++;
1258 if( p_ids->i_selected_programs == 1 )
1260 CAPMTFirst( p_access, i_session_id, p_pmt );
1261 return;
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 */,
1269 &i_capmt_size );
1271 if ( i_capmt_size )
1273 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1274 free( p_capmt );
1278 /*****************************************************************************
1279 * CAPMTUpdate
1280 *****************************************************************************/
1281 static void CAPMTUpdate( access_t * p_access, int i_session_id, uint8_t *p_pmt )
1283 uint8_t *p_capmt;
1284 int i_capmt_size;
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 */,
1291 &i_capmt_size );
1293 if ( i_capmt_size )
1295 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1296 free( p_capmt );
1300 /*****************************************************************************
1301 * CAPMTDelete
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;
1307 uint8_t *p_capmt;
1308 int i_capmt_size;
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 */,
1316 &i_capmt_size );
1318 if ( i_capmt_size )
1320 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1321 free( p_capmt );
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 );
1335 switch ( i_tag )
1337 case AOT_CA_INFO:
1339 int i;
1340 int l = 0;
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];
1353 d += 2;
1354 msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] );
1357 demux_ResendCAPMTs();
1358 break;
1361 case AOT_CA_UPDATE:
1362 /* http://www.cablelabs.com/specifications/OC-SP-HOSTPOD-IF-I08-011221.pdf */
1363 case AOT_CA_PMT_REPLY:
1364 /* We do not care */
1365 break;
1367 default:
1368 msg_Err( p_access,
1369 "unexpected tag in ConditionalAccessHandle (0x%x)",
1370 i_tag );
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 );
1403 * Date Time
1406 typedef struct
1408 int i_interval;
1409 mtime_t i_last;
1410 } date_time_t;
1412 /*****************************************************************************
1413 * DateTimeSend
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);
1421 struct tm tm_gmt;
1422 struct tm tm_loc;
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 /*****************************************************************************
1451 * DateTimeHandle
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 );
1461 switch ( i_tag )
1463 case AOT_DATE_TIME_ENQ:
1465 int l;
1466 const uint8_t *d = APDUGetLength( p_apdu, &l );
1468 if ( l > 0 )
1470 p_date->i_interval = *d;
1471 msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
1472 p_date->i_interval );
1474 else
1475 p_date->i_interval = 0;
1477 DateTimeSend( p_access, i_session_id );
1478 break;
1480 default:
1481 msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
1485 /*****************************************************************************
1486 * DateTimeManage
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 /*****************************************************************************
1501 * DateTimeClose
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 /*****************************************************************************
1511 * DateTimeOpen
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 );
1527 * MMI
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
1538 /* MMI Modes */
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
1555 /* Enquiry Flags */
1557 #define EF_BLIND 0x01
1559 /* Answer IDs */
1561 #define AI_CANCEL 0x00
1562 #define AI_ANSWER 0x01
1564 typedef struct
1566 en50221_mmi_object_t last_object;
1567 } mmi_t;
1569 static inline void en50221_MMIFree( en50221_mmi_object_t *p_object )
1571 int i;
1573 switch ( p_object->i_object_type )
1575 case EN50221_MMI_ENQ:
1576 free( p_object->u.enq.psz_text );
1577 break;
1579 case EN50221_MMI_ANSW:
1580 if ( p_object->u.answ.b_ok )
1582 free( p_object->u.answ.psz_answ );
1584 break;
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 );
1596 break;
1598 default:
1599 break;
1603 /*****************************************************************************
1604 * MMISendObject
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;
1610 uint8_t *p_data;
1611 int i_size, i_tag;
1613 switch ( p_object->i_object_type )
1615 case EN50221_MMI_ANSW:
1616 i_tag = AOT_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 );
1621 break;
1623 case EN50221_MMI_MENU_ANSW:
1624 i_tag = AOT_MENU_ANSW;
1625 i_size = 1;
1626 p_data = malloc( i_size );
1627 p_data[0] = p_object->u.menu_answ.i_choice;
1628 break;
1630 default:
1631 msg_Err( p_access, "unknown MMI object %d", p_object->i_object_type );
1632 return;
1635 APDUSend( p_access, i_session_id, i_tag, p_data, i_size );
1636 free( p_data );
1638 p_slots[i_slot].b_mmi_expected = true;
1641 /*****************************************************************************
1642 * MMISendClose
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 /*****************************************************************************
1654 * MMIDisplayReply
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 /*****************************************************************************
1669 * MMIGetText
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 );
1674 int l;
1675 uint8_t *d;
1677 if ( i_tag != AOT_TEXT_LAST )
1679 msg_Err( p_access, "unexpected text tag: %06x", i_tag );
1680 *pi_size = 0;
1681 return strdup( "" );
1684 d = APDUGetLength( *pp_apdu, &l );
1686 *pp_apdu += l + 4;
1687 *pi_size -= l + 4;
1689 return dvb_string_get( d, l, demux_Iconv, p_access );
1692 /*****************************************************************************
1693 * MMIHandleEnq
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;
1700 int l;
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 */
1707 l -= 2;
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 /*****************************************************************************
1720 * MMIHandleMenu
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;
1727 int l;
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;
1736 if ( l > 0 )
1738 l--; d++; /* choice_nb */
1740 #define GET_FIELD( x ) \
1741 if ( l > 0 ) \
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 ); \
1749 GET_FIELD( title );
1750 GET_FIELD( subtitle );
1751 GET_FIELD( bottom );
1752 #undef GET_FIELD
1754 while ( l > 0 )
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,
1759 psz_text );
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 /*****************************************************************************
1769 * MMIHandle
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 );
1776 switch ( i_tag )
1778 case AOT_DISPLAY_CONTROL:
1780 int l;
1781 uint8_t *d = APDUGetLength( p_apdu, &l );
1783 if ( l > 0 )
1785 switch ( *d )
1787 case DCC_SET_MMI_MODE:
1788 if ( l == 2 && d[1] == MM_HIGH_LEVEL )
1789 MMIDisplayReply( p_access, i_session_id );
1790 else
1791 msg_Err( p_access, "unsupported MMI mode %02x", d[1] );
1792 break;
1794 default:
1795 msg_Err( p_access, "unsupported display control command %02x",
1796 *d );
1797 break;
1800 break;
1803 case AOT_ENQ:
1804 MMIHandleEnq( p_access, i_session_id, p_apdu, i_size );
1805 break;
1807 case AOT_LIST_LAST:
1808 case AOT_MENU_LAST:
1809 MMIHandleMenu( p_access, i_session_id, i_tag, p_apdu, i_size );
1810 break;
1812 case AOT_CLOSE_MMI:
1813 SessionSendClose( p_access, i_session_id );
1814 break;
1816 default:
1817 msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
1821 /*****************************************************************************
1822 * MMIClose
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 /*****************************************************************************
1839 * MMIOpen
1840 *****************************************************************************/
1841 static void MMIOpen( access_t *p_access, int i_session_id )
1843 mmi_t *p_mmi;
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;
1856 * Hardware handling
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",
1866 i_slot );
1869 /*****************************************************************************
1870 * ResetSlot
1871 *****************************************************************************/
1872 static void ResetSlot( int i_slot )
1874 ci_slot_t *p_slot = &p_slots[i_slot];
1875 int i_session_id;
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 )
1924 char psz_tmp[128];
1925 ca_caps_t caps;
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) );
1934 i_ca_handle = 0;
1935 return;
1938 if ( ioctl( i_ca_handle, CA_GET_CAP, &caps ) != 0 )
1940 msg_Err( NULL, "failed getting CAM capabilities (%s)",
1941 strerror(errno) );
1942 close( i_ca_handle );
1943 i_ca_handle = 0;
1944 return;
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 );
1974 i_ca_handle = 0;
1975 return;
1978 if( caps.slot_type & CA_CI_LINK )
1979 i_ca_type = CA_CI_LINK;
1980 else if( caps.slot_type & CA_CI )
1981 i_ca_type = CA_CI;
1982 else
1984 msg_Err( NULL, "Incompatible CAM interface" );
1985 close( i_ca_handle );
1986 i_ca_handle = 0;
1987 return;
1990 i_nb_slots = caps.slot_num;
1991 memset( p_sessions, 0, sizeof(en50221_session_t) * MAX_SESSIONS );
1993 en50221_Reset();
1996 /*****************************************************************************
1997 * en50221_Reset : Reset the CAM for en50221
1998 *****************************************************************************/
1999 void en50221_Reset( void )
2001 switch (i_print_type)
2003 case PRINT_XML:
2004 printf("<STATUS type=\"cam\" status=\"0\" />\n");
2005 break;
2006 default:
2007 break;
2010 memset( p_slots, 0, sizeof(ci_slot_t) * MAX_CI_SLOTS );
2012 if( i_ca_type & CA_CI_LINK )
2014 int i_slot;
2015 for ( i_slot = 0; i_slot < i_nb_slots; i_slot++ )
2016 ResetSlot( i_slot );
2018 else
2020 struct ca_slot_info info;
2021 system_ids_t *p_ids;
2022 ca_msg_t ca_msg;
2023 info.num = 0;
2025 /* We don't reset the CAM in that case because it's done by the
2026 * ASIC. */
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 );
2031 i_ca_handle = 0;
2032 return;
2034 if( info.flags == 0 )
2036 msg_Err( NULL, "en50221_Init: no CAM inserted" );
2037 close( i_ca_handle );
2038 i_ca_handle = 0;
2039 return;
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 */
2053 ca_msg.length=3;
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 );
2063 i_ca_handle = 0;
2064 return;
2067 #ifdef HLCI_WAIT_CAM_READY
2068 while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
2070 msleep(1);
2071 msg_Dbg( NULL, "CAM: please wait" );
2072 APDUSend( NULL, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
2073 ca_msg.length=3;
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 );
2082 i_ca_handle = 0;
2083 return;
2085 msg_Dbg( NULL, "en50221_Init: Got length: %d, tag: 0x%x", ca_msg.length, APDUGetTag( ca_msg.msg, ca_msg.length ) );
2087 #else
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 );
2092 i_ca_handle = 0;
2093 return;
2095 #endif
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 )
2106 TPDURecv( NULL );
2109 /*****************************************************************************
2110 * en50221_Poll : Send a poll TPDU to the CAM
2111 *****************************************************************************/
2112 void en50221_Poll( void )
2114 int i_slot;
2115 int i_session_id;
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;
2123 sinfo.num = i_slot;
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",
2127 i_slot );
2128 continue;
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",
2136 i_slot );
2137 ResetSlot( i_slot );
2140 continue;
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 );
2150 continue;
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 )
2173 msg_Err( NULL,
2174 "en50221_Poll: couldn't send TPDU on slot %d, resetting",
2175 i_slot );
2176 ResetSlot( i_slot );
2182 /*****************************************************************************
2183 * en50221_AddPMT :
2184 *****************************************************************************/
2185 void en50221_AddPMT( uint8_t *p_pmt )
2187 int i_session_id;
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 )
2200 int i_session_id;
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 )
2213 int i_session_id;
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) );
2231 return RET_ERR;
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 )
2244 int i_slot;
2245 struct ret_mmi_slot_status *p_ret = (struct ret_mmi_slot_status *)p_answer;
2247 if ( i_size != 1 ) return RET_HUH;
2248 i_slot = *p_buffer;
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) );
2254 return RET_ERR;
2257 *pi_size = sizeof(struct ret_mmi_slot_status);
2258 return RET_MMI_SLOT_STATUS;
2261 /*****************************************************************************
2262 * en50221_OpenMMI :
2263 *****************************************************************************/
2264 uint8_t en50221_OpenMMI( uint8_t *p_buffer, ssize_t i_size )
2266 int i_slot;
2268 if ( i_size != 1 ) return RET_HUH;
2269 i_slot = *p_buffer;
2271 if( i_ca_type & CA_CI_LINK )
2273 int i_session_id;
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 )
2279 msg_Dbg( NULL,
2280 "MMI menu is already opened on slot %d (session=%d)",
2281 i_slot, i_session_id );
2282 return RET_OK;
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 );
2293 return RET_OK;
2297 msg_Err( NULL, "no application information on slot %d", i_slot );
2298 return RET_ERR;
2300 else
2302 msg_Err( NULL, "MMI menu not supported" );
2303 return RET_ERR;
2307 /*****************************************************************************
2308 * en50221_CloseMMI :
2309 *****************************************************************************/
2310 uint8_t en50221_CloseMMI( uint8_t *p_buffer, ssize_t i_size )
2312 int i_slot;
2314 if ( i_size != 1 ) return RET_HUH;
2315 i_slot = *p_buffer;
2317 if( i_ca_type & CA_CI_LINK )
2319 int i_session_id;
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 );
2326 return RET_OK;
2330 msg_Warn( NULL, "closing a non-existing MMI session on slot %d",
2331 i_slot );
2332 return RET_ERR;
2334 else
2336 msg_Err( NULL, "MMI menu not supported" );
2337 return RET_ERR;
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;
2351 i_slot = *p_buffer;
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 )
2364 mmi_t *p_mmi =
2365 (mmi_t *)p_sessions[i_session_id - 1].p_sys;
2366 if ( p_mmi == NULL )
2368 *pi_size = 0;
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 )
2377 *pi_size = 0;
2378 msg_Err( NULL, "MMI structure too big" );
2379 return RET_ERR;
2381 *pi_size += ((void *)&p_ret->object - (void *)p_ret);
2382 break;
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 )
2400 return RET_ERR;
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 );
2410 return RET_OK;
2414 msg_Err( NULL, "SendMMIObject when no MMI session is opened !" );
2415 return RET_ERR;