Merge branch 'nto-signal-stats'
[dvblast.git] / en50221.c
blob7c1be490d12b1e766a05cd9000454467fc074796
1 /*****************************************************************************
2 * en50221.c : implementation of the transport, session and applications
3 * layers of EN 50 221
4 *****************************************************************************
5 * Copyright (C) 2004-2005, 2010, 2015 VideoLAN
7 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8 * Based on code from libdvbci Copyright (C) 2000 Klaus Schmidinger
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
24 #include "config.h"
26 #ifdef HAVE_DVB_SUPPORT
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <stdbool.h>
31 #include <stddef.h>
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <inttypes.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <sys/ioctl.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include <poll.h>
44 #include <errno.h>
45 #include <time.h>
46 #include <ev.h>
48 /* DVB Card Drivers */
49 #include <linux/dvb/version.h>
50 #include <linux/dvb/dmx.h>
51 #include <linux/dvb/frontend.h>
52 #include <linux/dvb/ca.h>
54 #include <bitstream/mpeg/psi.h>
55 #include <bitstream/dvb/ci.h>
56 #include <bitstream/dvb/si.h>
58 #include "dvblast.h"
59 #include "en50221.h"
60 #include "comm.h"
62 #define TAB_APPEND( count, tab, p ) \
63 if( (count) > 0 ) \
64 { \
65 (tab) = realloc( tab, sizeof( void ** ) * ( (count) + 1 ) ); \
66 } \
67 else \
68 { \
69 (tab) = malloc( sizeof( void ** ) ); \
70 } \
71 (tab)[count] = (p); \
72 (count)++
74 /*****************************************************************************
75 * Local declarations
76 *****************************************************************************/
77 #undef DEBUG_TPDU
78 #define CAM_INIT_TIMEOUT 15000000 /* 15 s */
79 #undef HLCI_WAIT_CAM_READY
80 #define CAPMT_WAIT 100 /* ms */
81 #define CA_POLL_PERIOD 100000 /* 100 ms */
83 typedef struct en50221_msg_t
85 uint8_t *p_data;
86 int i_size;
87 struct en50221_msg_t *p_next;
88 } en50221_msg_t;
90 typedef struct en50221_session_t
92 int i_slot;
93 int i_resource_id;
94 void (* pf_handle)( access_t *, int, uint8_t *, int );
95 void (* pf_close)( access_t *, int );
96 void (* pf_manage)( access_t *, int );
97 void *p_sys;
98 } en50221_session_t;
100 typedef struct ci_slot_t
102 bool b_active;
103 bool b_expect_answer;
104 bool b_has_data;
105 bool b_mmi_expected;
106 bool b_mmi_undisplayed;
108 /* TPDU reception */
109 en50221_msg_t *p_recv;
111 /* TPDU emission */
112 en50221_msg_t *p_send;
113 en50221_msg_t **pp_send_last;
114 uint8_t *p;
116 /* InitSlot timer */
117 struct ev_timer init_watcher;
119 /* SPDUSend callback, if p_spdu is not NULL */
120 /* SessionOpen callback, if not 0 */
121 int i_pending_session_id;
122 } ci_slot_t;
124 int i_ca_handle = 0;
125 int i_ca_type = -1;
127 static struct ev_io cam_watcher;
128 static struct ev_timer slot_watcher;
129 static int i_nb_slots = 0;
130 static ci_slot_t p_slots[MAX_CI_SLOTS];
131 static en50221_session_t p_sessions[MAX_SESSIONS];
133 /*****************************************************************************
134 * Local prototypes
135 *****************************************************************************/
136 static void SessionOpenCb( access_t *p_access, uint8_t i_slot );
137 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
138 uint8_t *p_spdu, int i_size );
140 static void ResourceManagerOpen( access_t * p_access, int i_session_id );
141 static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
142 static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
143 static void DateTimeOpen( access_t * p_access, int i_session_id );
144 static void ResetSlotCb(struct ev_loop *loop, struct ev_timer *w, int revents);
145 static void en50221_Read(struct ev_loop *loop, struct ev_io *w, int revents);
146 static void en50221_Poll(struct ev_loop *loop, struct ev_timer *w, int revents);
147 static void MMIOpen( access_t * p_access, int i_session_id );
149 /*****************************************************************************
150 * Utility functions
151 *****************************************************************************/
152 #define SIZE_INDICATOR 0x80
154 static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
156 *pi_length = *p_data++;
158 if ( (*pi_length & SIZE_INDICATOR) != 0 )
160 int l = *pi_length & ~SIZE_INDICATOR;
161 int i;
163 *pi_length = 0;
164 for ( i = 0; i < l; i++ )
165 *pi_length = (*pi_length << 8) | *p_data++;
168 return p_data;
171 static uint8_t *SetLength( uint8_t *p_data, int i_length )
173 uint8_t *p = p_data;
175 if ( i_length < 128 )
177 *p++ = i_length;
179 else if ( i_length < 256 )
181 *p++ = SIZE_INDICATOR | 0x1;
182 *p++ = i_length;
184 else if ( i_length < 65536 )
186 *p++ = SIZE_INDICATOR | 0x2;
187 *p++ = i_length >> 8;
188 *p++ = i_length & 0xff;
190 else if ( i_length < 16777216 )
192 *p++ = SIZE_INDICATOR | 0x3;
193 *p++ = i_length >> 16;
194 *p++ = (i_length >> 8) & 0xff;
195 *p++ = i_length & 0xff;
197 else
199 *p++ = SIZE_INDICATOR | 0x4;
200 *p++ = i_length >> 24;
201 *p++ = (i_length >> 16) & 0xff;
202 *p++ = (i_length >> 8) & 0xff;
203 *p++ = i_length & 0xff;
206 return p;
211 * Transport layer
214 #define MAX_TPDU_SIZE 4096
215 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 7)
217 #define DATA_INDICATOR 0x80
219 #define T_SB 0x80
220 #define T_RCV 0x81
221 #define T_CREATE_TC 0x82
222 #define T_CTC_REPLY 0x83
223 #define T_DELETE_TC 0x84
224 #define T_DTC_REPLY 0x85
225 #define T_REQUEST_TC 0x86
226 #define T_NEW_TC 0x87
227 #define T_TC_ERROR 0x88
228 #define T_DATA_LAST 0xA0
229 #define T_DATA_MORE 0xA1
231 static void Dump( bool b_outgoing, uint8_t *p_data, int i_size )
233 #ifdef DEBUG_TPDU
234 int i;
235 #define MAX_DUMP 256
236 fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
237 for ( i = 0; i < i_size && i < MAX_DUMP; i++)
238 fprintf(stderr, "%02X ", p_data[i]);
239 fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
240 #endif
243 /*****************************************************************************
244 * TPDUWrite
245 *****************************************************************************/
246 static int TPDUWrite( access_t * p_access, uint8_t i_slot )
248 ci_slot_t *p_slot = &p_slots[i_slot];
249 en50221_msg_t *p_send = p_slot->p_send;
251 if ( p_slot->b_expect_answer )
252 msg_Warn( p_access,
253 "en50221: writing while expecting an answer on slot %u",
254 i_slot );
255 if ( p_send == NULL )
257 msg_Warn( p_access, "en50221: no data to write on slot %u !", i_slot );
258 return -1;
260 p_slot->p_send = p_send->p_next;
261 if ( p_slot->p_send == NULL )
262 p_slot->pp_send_last = &p_slot->p_send;
264 Dump( true, p_send->p_data, p_send->i_size );
266 if ( write( i_ca_handle, p_send->p_data, p_send->i_size )
267 != p_send->i_size )
269 msg_Err( p_access, "en50221: cannot write to CAM device (%m)" );
270 free( p_send->p_data );
271 free( p_send );
272 return -1;
275 free( p_send->p_data );
276 free( p_send );
277 p_slot->b_expect_answer = true;
279 return 0;
282 /*****************************************************************************
283 * TPDUSend
284 *****************************************************************************/
285 static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
286 const uint8_t *p_content, int i_length )
288 ci_slot_t *p_slot = &p_slots[i_slot];
289 uint8_t i_tcid = i_slot + 1;
290 en50221_msg_t *p_send = malloc( sizeof(en50221_msg_t) );
291 uint8_t *p_data = malloc( MAX_TPDU_SIZE );
292 int i_size;
294 i_size = 0;
295 p_data[0] = i_slot;
296 p_data[1] = i_tcid;
297 p_data[2] = i_tag;
299 switch ( i_tag )
301 case T_RCV:
302 case T_CREATE_TC:
303 case T_CTC_REPLY:
304 case T_DELETE_TC:
305 case T_DTC_REPLY:
306 case T_REQUEST_TC:
307 p_data[3] = 1; /* length */
308 p_data[4] = i_tcid;
309 i_size = 5;
310 break;
312 case T_NEW_TC:
313 case T_TC_ERROR:
314 p_data[3] = 2; /* length */
315 p_data[4] = i_tcid;
316 p_data[5] = p_content[0];
317 i_size = 6;
318 break;
320 case T_DATA_LAST:
321 case T_DATA_MORE:
323 /* i_length <= MAX_TPDU_DATA */
324 uint8_t *p = p_data + 3;
325 p = SetLength( p, i_length + 1 );
326 *p++ = i_tcid;
328 if ( i_length )
329 memcpy( p, p_content, i_length );
330 i_size = i_length + (p - p_data);
331 break;
334 default:
335 break;
338 p_send->p_data = p_data;
339 p_send->i_size = i_size;
340 p_send->p_next = NULL;
342 *p_slot->pp_send_last = p_send;
343 p_slot->pp_send_last = &p_send->p_next;
345 if ( !p_slot->b_expect_answer )
346 return TPDUWrite( p_access, i_slot );
348 return 0;
352 /*****************************************************************************
353 * TPDURecv
354 *****************************************************************************/
355 static int TPDURecv( access_t * p_access )
357 ci_slot_t *p_slot;
358 uint8_t i_tag, i_slot;
359 uint8_t p_data[MAX_TPDU_SIZE];
360 int i_size;
361 bool b_last = false;
365 i_size = read( i_ca_handle, p_data, MAX_TPDU_SIZE );
367 while ( i_size < 0 && errno == EINTR );
369 if ( i_size < 5 )
371 msg_Err( p_access, "en50221: cannot read from CAM device (%d:%m)",
372 i_size );
373 return -1;
376 Dump( false, p_data, i_size );
378 i_slot = p_data[1] - 1;
379 i_tag = p_data[2];
381 if ( i_slot >= i_nb_slots )
383 msg_Warn( p_access, "en50221: TPDU is from an unknown slot %u",
384 i_slot );
385 return -1;
387 p_slot = &p_slots[i_slot];
389 p_slot->b_has_data = !!(p_data[i_size - 4] == T_SB
390 && p_data[i_size - 3] == 2
391 && (p_data[i_size - 1] & DATA_INDICATOR));
392 p_slot->b_expect_answer = false;
394 switch ( i_tag )
396 case T_CTC_REPLY:
397 p_slot->b_active = true;
398 ev_timer_stop(event_loop, &p_slot->init_watcher);
399 msg_Dbg( p_access, "CI slot %d is active", i_slot );
400 break;
402 case T_SB:
403 break;
405 case T_DATA_LAST:
406 b_last = true;
407 /* intended pass-through */
408 case T_DATA_MORE:
410 en50221_msg_t *p_recv;
411 int i_session_size;
412 uint8_t *p_session = GetLength( &p_data[3], &i_session_size );
414 if ( i_session_size <= 1 )
415 break;
416 p_session++;
417 i_session_size--;
419 if ( p_slot->p_recv == NULL )
421 p_slot->p_recv = malloc( sizeof(en50221_msg_t) );
422 p_slot->p_recv->p_data = NULL;
423 p_slot->p_recv->i_size = 0;
426 p_recv = p_slot->p_recv;
427 p_recv->p_data = realloc( p_recv->p_data,
428 p_recv->i_size + i_session_size );
429 memcpy( &p_recv->p_data[ p_recv->i_size ], p_session, i_session_size );
430 p_recv->i_size += i_session_size;
432 if ( b_last )
434 SPDUHandle( p_access, i_slot, p_recv->p_data, p_recv->i_size );
435 free( p_recv->p_data );
436 free( p_recv );
437 p_slot->p_recv = NULL;
439 break;
442 default:
443 msg_Warn( p_access, "en50221: unhandled R_TPDU tag %u slot %u", i_tag,
444 i_slot );
445 break;
448 if ( !p_slot->b_expect_answer && p_slot->p_send != NULL )
449 TPDUWrite( p_access, i_slot );
450 if ( !p_slot->b_expect_answer && p_slot->i_pending_session_id != 0 )
451 SessionOpenCb( p_access, i_slot );
452 if ( !p_slot->b_expect_answer && p_slot->b_has_data )
453 TPDUSend( p_access, i_slot, T_RCV, NULL, 0 );
455 return 0;
460 * Session layer
463 #define ST_SESSION_NUMBER 0x90
464 #define ST_OPEN_SESSION_REQUEST 0x91
465 #define ST_OPEN_SESSION_RESPONSE 0x92
466 #define ST_CREATE_SESSION 0x93
467 #define ST_CREATE_SESSION_RESPONSE 0x94
468 #define ST_CLOSE_SESSION_REQUEST 0x95
469 #define ST_CLOSE_SESSION_RESPONSE 0x96
471 #define SS_OK 0x00
472 #define SS_NOT_ALLOCATED 0xF0
474 #define RI_RESOURCE_MANAGER 0x00010041
475 #define RI_APPLICATION_INFORMATION 0x00020041
476 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
477 #define RI_HOST_CONTROL 0x00200041
478 #define RI_DATE_TIME 0x00240041
479 #define RI_MMI 0x00400041
481 static int ResourceIdToInt( uint8_t *p_data )
483 return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
484 | ((int)p_data[2] << 8) | p_data[3];
487 /*****************************************************************************
488 * SPDUSend
489 *****************************************************************************/
490 static int SPDUSend( access_t *p_access, int i_session_id,
491 uint8_t *p_data, int i_size )
493 uint8_t *p_spdu = malloc( i_size + 4 );
494 uint8_t *p = p_spdu;
495 uint8_t i_slot = p_sessions[i_session_id - 1].i_slot;
497 *p++ = ST_SESSION_NUMBER;
498 *p++ = 0x02;
499 *p++ = (i_session_id >> 8);
500 *p++ = i_session_id & 0xff;
502 memcpy( p, p_data, i_size );
504 i_size += 4;
505 p = p_spdu;
507 while ( i_size > 0 )
509 if ( i_size > MAX_TPDU_DATA )
511 if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
512 MAX_TPDU_DATA ) != 0 )
514 msg_Err( p_access, "couldn't send TPDU on session %d",
515 i_session_id );
516 free( p_spdu );
517 return -1;
519 p += MAX_TPDU_DATA;
520 i_size -= MAX_TPDU_DATA;
522 else
524 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
525 != 0 )
527 msg_Err( p_access, "couldn't send TPDU on session %d",
528 i_session_id );
529 free( p_spdu );
530 return -1;
532 i_size = 0;
536 free( p_spdu );
537 return 0;
540 /*****************************************************************************
541 * SessionOpen
542 *****************************************************************************/
543 static void SessionOpenCb( access_t *p_access, uint8_t i_slot )
545 ci_slot_t *p_slot = &p_slots[i_slot];
546 int i_session_id = p_slot->i_pending_session_id;
547 int i_resource_id = p_sessions[i_session_id - 1].i_resource_id;
549 p_slot->i_pending_session_id = 0;
551 switch ( i_resource_id )
553 case RI_RESOURCE_MANAGER:
554 ResourceManagerOpen( p_access, i_session_id ); break;
555 case RI_APPLICATION_INFORMATION:
556 ApplicationInformationOpen( p_access, i_session_id ); break;
557 case RI_CONDITIONAL_ACCESS_SUPPORT:
558 ConditionalAccessOpen( p_access, i_session_id ); break;
559 case RI_DATE_TIME:
560 DateTimeOpen( p_access, i_session_id ); break;
561 case RI_MMI:
562 MMIOpen( p_access, i_session_id ); break;
564 case RI_HOST_CONTROL:
565 default:
566 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
567 p_sessions[i_session_id - 1].i_resource_id = 0;
571 static void SessionOpen( access_t * p_access, uint8_t i_slot,
572 uint8_t *p_spdu, int i_size )
574 ci_slot_t *p_slot = &p_slots[i_slot];
575 int i_session_id;
576 int i_resource_id = ResourceIdToInt( &p_spdu[2] );
577 uint8_t p_response[16];
578 int i_status = SS_NOT_ALLOCATED;
580 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
582 if ( !p_sessions[i_session_id - 1].i_resource_id )
583 break;
585 if ( i_session_id > MAX_SESSIONS )
587 msg_Err( p_access, "too many sessions !" );
588 return;
590 p_sessions[i_session_id - 1].i_slot = i_slot;
591 p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
592 p_sessions[i_session_id - 1].pf_close = NULL;
593 p_sessions[i_session_id - 1].pf_manage = NULL;
595 if ( i_resource_id == RI_RESOURCE_MANAGER
596 || i_resource_id == RI_APPLICATION_INFORMATION
597 || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
598 || i_resource_id == RI_DATE_TIME
599 || i_resource_id == RI_MMI )
601 i_status = SS_OK;
604 p_response[0] = ST_OPEN_SESSION_RESPONSE;
605 p_response[1] = 0x7;
606 p_response[2] = i_status;
607 p_response[3] = p_spdu[2];
608 p_response[4] = p_spdu[3];
609 p_response[5] = p_spdu[4];
610 p_response[6] = p_spdu[5];
611 p_response[7] = i_session_id >> 8;
612 p_response[8] = i_session_id & 0xff;
614 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) != 0 )
616 msg_Err( p_access,
617 "SessionOpen: couldn't send TPDU on slot %d", i_slot );
618 return;
621 if ( p_slot->i_pending_session_id != 0 )
622 msg_Warn( p_access, "overwriting pending session %d",
623 p_slot->i_pending_session_id );
624 p_slot->i_pending_session_id = i_session_id;
627 #if 0
628 /* unused code for the moment - commented out to keep gcc happy */
629 /*****************************************************************************
630 * SessionCreate
631 *****************************************************************************/
632 static void SessionCreate( access_t * p_access, int i_slot, int i_resource_id )
634 uint8_t p_response[16];
635 uint8_t i_tag;
636 int i_session_id;
638 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
640 if ( !p_sessions[i_session_id - 1].i_resource_id )
641 break;
643 if ( i_session_id > MAX_SESSIONS )
645 msg_Err( p_access, "too many sessions !" );
646 return;
648 p_sessions[i_session_id - 1].i_slot = i_slot;
649 p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
650 p_sessions[i_session_id - 1].pf_close = NULL;
651 p_sessions[i_session_id - 1].pf_manage = NULL;
652 p_sessions[i_session_id - 1].p_sys = NULL;
654 p_response[0] = ST_CREATE_SESSION;
655 p_response[1] = 0x6;
656 p_response[2] = i_resource_id >> 24;
657 p_response[3] = (i_resource_id >> 16) & 0xff;
658 p_response[4] = (i_resource_id >> 8) & 0xff;
659 p_response[5] = i_resource_id & 0xff;
660 p_response[6] = i_session_id >> 8;
661 p_response[7] = i_session_id & 0xff;
663 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
666 msg_Err( p_access,
667 "SessionCreate: couldn't send TPDU on slot %d", i_slot );
668 return;
671 #endif
673 /*****************************************************************************
674 * SessionCreateResponse
675 *****************************************************************************/
676 static void SessionCreateResponse( access_t * p_access, uint8_t i_slot,
677 uint8_t *p_spdu, int i_size )
679 int i_status = p_spdu[2];
680 int i_resource_id = ResourceIdToInt( &p_spdu[3] );
681 int i_session_id = ((int)p_spdu[7] << 8) | p_spdu[8];
683 if ( i_status != SS_OK )
685 msg_Err( p_access, "SessionCreateResponse: failed to open session %d"
686 " resource=0x%x status=0x%x", i_session_id, i_resource_id,
687 i_status );
688 p_sessions[i_session_id - 1].i_resource_id = 0;
689 return;
692 switch ( i_resource_id )
694 case RI_RESOURCE_MANAGER:
695 ResourceManagerOpen( p_access, i_session_id ); break;
696 case RI_APPLICATION_INFORMATION:
697 ApplicationInformationOpen( p_access, i_session_id ); break;
698 case RI_CONDITIONAL_ACCESS_SUPPORT:
699 ConditionalAccessOpen( p_access, i_session_id ); break;
700 case RI_DATE_TIME:
701 DateTimeOpen( p_access, i_session_id ); break;
702 case RI_MMI:
703 MMIOpen( p_access, i_session_id ); break;
705 case RI_HOST_CONTROL:
706 default:
707 msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
708 p_sessions[i_session_id - 1].i_resource_id = 0;
712 /*****************************************************************************
713 * SessionSendClose
714 *****************************************************************************/
715 static void SessionSendClose( access_t * p_access, int i_session_id )
717 uint8_t p_response[16];
718 uint8_t i_slot = p_sessions[i_session_id - 1].i_slot;
720 p_response[0] = ST_CLOSE_SESSION_REQUEST;
721 p_response[1] = 0x2;
722 p_response[2] = i_session_id >> 8;
723 p_response[3] = i_session_id & 0xff;
725 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
728 msg_Err( p_access,
729 "SessionSendClose: couldn't send TPDU on slot %d", i_slot );
730 return;
734 /*****************************************************************************
735 * SessionClose
736 *****************************************************************************/
737 static void SessionClose( access_t * p_access, int i_session_id )
739 uint8_t p_response[16];
740 uint8_t i_slot = p_sessions[i_session_id - 1].i_slot;
742 if ( p_sessions[i_session_id - 1].pf_close != NULL )
743 p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
744 p_sessions[i_session_id - 1].i_resource_id = 0;
746 p_response[0] = ST_CLOSE_SESSION_RESPONSE;
747 p_response[1] = 0x3;
748 p_response[2] = SS_OK;
749 p_response[3] = i_session_id >> 8;
750 p_response[4] = i_session_id & 0xff;
752 if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
755 msg_Err( p_access,
756 "SessionClose: couldn't send TPDU on slot %d", i_slot );
757 return;
761 /*****************************************************************************
762 * SPDUHandle
763 *****************************************************************************/
764 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
765 uint8_t *p_spdu, int i_size )
767 int i_session_id;
769 switch ( p_spdu[0] )
771 case ST_SESSION_NUMBER:
772 if ( i_size <= 4 )
773 return;
774 i_session_id = (p_spdu[2] << 8) | p_spdu[3];
775 if ( i_session_id <= MAX_SESSIONS
776 && p_sessions[i_session_id - 1].pf_handle != NULL )
777 p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
778 p_spdu + 4, i_size - 4 );
779 break;
781 case ST_OPEN_SESSION_REQUEST:
782 if ( i_size != 6 || p_spdu[1] != 0x4 )
783 return;
784 SessionOpen( p_access, i_slot, p_spdu, i_size );
785 break;
787 case ST_CREATE_SESSION_RESPONSE:
788 if ( i_size != 9 || p_spdu[1] != 0x7 )
789 return;
790 SessionCreateResponse( p_access, i_slot, p_spdu, i_size );
791 break;
793 case ST_CLOSE_SESSION_REQUEST:
794 if ( i_size != 4 || p_spdu[1] != 0x2 )
795 return;
796 i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
797 SessionClose( p_access, i_session_id );
798 break;
800 case ST_CLOSE_SESSION_RESPONSE:
801 if ( i_size != 5 || p_spdu[1] != 0x3 )
802 return;
803 i_session_id = ((int)p_spdu[3] << 8) | p_spdu[4];
804 if ( p_spdu[2] )
806 msg_Err( p_access, "closing a session which is not allocated (%d)",
807 i_session_id );
809 else
811 if ( p_sessions[i_session_id - 1].pf_close != NULL )
812 p_sessions[i_session_id - 1].pf_close( p_access,
813 i_session_id );
814 p_sessions[i_session_id - 1].i_resource_id = 0;
816 break;
818 default:
819 msg_Err( p_access, "unexpected tag in SPDUHandle (%x)", p_spdu[0] );
820 break;
826 * Application layer
829 #define AOT_NONE 0x000000
830 #define AOT_PROFILE_ENQ 0x9F8010
831 #define AOT_PROFILE 0x9F8011
832 #define AOT_PROFILE_CHANGE 0x9F8012
833 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
834 #define AOT_APPLICATION_INFO 0x9F8021
835 #define AOT_ENTER_MENU 0x9F8022
836 #define AOT_CA_INFO_ENQ 0x9F8030
837 #define AOT_CA_INFO 0x9F8031
838 #define AOT_CA_PMT 0x9F8032
839 #define AOT_CA_PMT_REPLY 0x9F8033
840 #define AOT_CA_UPDATE 0x9F8034
841 #define AOT_TUNE 0x9F8400
842 #define AOT_REPLACE 0x9F8401
843 #define AOT_CLEAR_REPLACE 0x9F8402
844 #define AOT_ASK_RELEASE 0x9F8403
845 #define AOT_DATE_TIME_ENQ 0x9F8440
846 #define AOT_DATE_TIME 0x9F8441
847 #define AOT_CLOSE_MMI 0x9F8800
848 #define AOT_DISPLAY_CONTROL 0x9F8801
849 #define AOT_DISPLAY_REPLY 0x9F8802
850 #define AOT_TEXT_LAST 0x9F8803
851 #define AOT_TEXT_MORE 0x9F8804
852 #define AOT_KEYPAD_CONTROL 0x9F8805
853 #define AOT_KEYPRESS 0x9F8806
854 #define AOT_ENQ 0x9F8807
855 #define AOT_ANSW 0x9F8808
856 #define AOT_MENU_LAST 0x9F8809
857 #define AOT_MENU_MORE 0x9F880A
858 #define AOT_MENU_ANSW 0x9F880B
859 #define AOT_LIST_LAST 0x9F880C
860 #define AOT_LIST_MORE 0x9F880D
861 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
862 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
863 #define AOT_DISPLAY_MESSAGE 0x9F8810
864 #define AOT_SCENE_END_MARK 0x9F8811
865 #define AOT_SCENE_DONE 0x9F8812
866 #define AOT_SCENE_CONTROL 0x9F8813
867 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
868 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
869 #define AOT_FLUSH_DOWNLOAD 0x9F8816
870 #define AOT_DOWNLOAD_REPLY 0x9F8817
871 #define AOT_COMMS_CMD 0x9F8C00
872 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
873 #define AOT_COMMS_REPLY 0x9F8C02
874 #define AOT_COMMS_SEND_LAST 0x9F8C03
875 #define AOT_COMMS_SEND_MORE 0x9F8C04
876 #define AOT_COMMS_RCV_LAST 0x9F8C05
877 #define AOT_COMMS_RCV_MORE 0x9F8C06
879 /*****************************************************************************
880 * APDUGetTag
881 *****************************************************************************/
882 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
884 if ( i_size >= 3 )
886 int i, t = 0;
887 for ( i = 0; i < 3; i++ )
888 t = (t << 8) | *p_apdu++;
889 return t;
892 return AOT_NONE;
895 /*****************************************************************************
896 * APDUGetLength
897 *****************************************************************************/
898 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
900 return GetLength( &p_apdu[3], pi_size );
903 /*****************************************************************************
904 * APDUSend
905 *****************************************************************************/
906 static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
907 uint8_t *p_data, int i_size )
909 uint8_t *p_apdu = malloc( i_size + 12 );
910 uint8_t *p = p_apdu;
911 ca_msg_t ca_msg;
912 int i_ret;
914 *p++ = (i_tag >> 16);
915 *p++ = (i_tag >> 8) & 0xff;
916 *p++ = i_tag & 0xff;
917 p = SetLength( p, i_size );
918 if ( i_size )
919 memcpy( p, p_data, i_size );
920 if ( i_ca_type == CA_CI_LINK )
922 i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
924 else
926 if ( i_size + p - p_apdu > 256 )
928 msg_Err( p_access, "CAM: apdu overflow" );
929 i_ret = -1;
931 else
933 ca_msg.length = i_size + p - p_apdu;
934 if ( i_size == 0 ) ca_msg.length=3;
935 memcpy( ca_msg.msg, p_apdu, i_size + p - p_apdu );
936 i_ret = ioctl(i_ca_handle, CA_SEND_MSG, &ca_msg );
937 if ( i_ret < 0 )
939 msg_Err( p_access, "Error sending to CAM: %m" );
940 i_ret = -1;
944 free( p_apdu );
945 return i_ret;
949 * Resource Manager
952 /*****************************************************************************
953 * ResourceManagerHandle
954 *****************************************************************************/
955 static void ResourceManagerHandle( access_t * p_access, int i_session_id,
956 uint8_t *p_apdu, int i_size )
958 int i_tag = APDUGetTag( p_apdu, i_size );
960 switch ( i_tag )
962 case AOT_PROFILE_ENQ:
964 int resources[] = { htonl(RI_RESOURCE_MANAGER),
965 htonl(RI_APPLICATION_INFORMATION),
966 htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
967 htonl(RI_DATE_TIME),
968 htonl(RI_MMI)
970 APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
971 sizeof(resources) );
972 break;
974 case AOT_PROFILE:
975 APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
976 break;
978 default:
979 msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
980 i_tag );
984 /*****************************************************************************
985 * ResourceManagerOpen
986 *****************************************************************************/
987 static void ResourceManagerOpen( access_t * p_access, int i_session_id )
990 msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
992 p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
994 APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
998 * Application Information
1001 /*****************************************************************************
1002 * ApplicationInformationEnterMenu
1003 *****************************************************************************/
1004 static void ApplicationInformationEnterMenu( access_t * p_access,
1005 int i_session_id )
1007 int i_slot = p_sessions[i_session_id - 1].i_slot;
1009 msg_Dbg( p_access, "entering MMI menus on session %d", i_session_id );
1010 APDUSend( p_access, i_session_id, AOT_ENTER_MENU, NULL, 0 );
1011 p_slots[i_slot].b_mmi_expected = true;
1014 /*****************************************************************************
1015 * ApplicationInformationHandle
1016 *****************************************************************************/
1017 static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
1018 uint8_t *p_apdu, int i_size )
1020 int i_tag = APDUGetTag( p_apdu, i_size );
1022 switch ( i_tag )
1024 case AOT_APPLICATION_INFO:
1026 int i_type, i_manufacturer, i_code;
1027 int l = 0;
1028 uint8_t *d = APDUGetLength( p_apdu, &l );
1030 if ( l < 4 ) break;
1032 i_type = *d++;
1033 i_manufacturer = ((int)d[0] << 8) | d[1];
1034 d += 2;
1035 i_code = ((int)d[0] << 8) | d[1];
1036 d += 2;
1037 d = GetLength( d, &l );
1040 char *psz_name = malloc(l + 1);
1041 memcpy( psz_name, d, l );
1042 psz_name[l] = '\0';
1043 msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
1044 psz_name, i_type, i_manufacturer, i_code );
1045 switch (i_print_type)
1047 case PRINT_XML:
1048 psz_name = dvb_string_xml_escape(psz_name);
1049 fprintf(print_fh, "<STATUS type=\"cam\" status=\"1\" cam_name=\"%s\" cam_type=\"%d\" cam_manufacturer=\"%d\" cam_product=\"%d\" />\n",
1050 psz_name, i_type, i_manufacturer, i_code);
1051 break;
1052 case PRINT_TEXT:
1053 fprintf(print_fh, "CAM name: %s type: %d manufacturer: %d product: %d\n",
1054 psz_name, i_type, i_manufacturer, i_code);
1055 break;
1056 default:
1057 break;
1059 free(psz_name);
1061 break;
1063 default:
1064 msg_Err( p_access,
1065 "unexpected tag in ApplicationInformationHandle (0x%x)",
1066 i_tag );
1070 /*****************************************************************************
1071 * ApplicationInformationOpen
1072 *****************************************************************************/
1073 static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
1076 msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
1078 p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
1080 APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1084 * Conditional Access
1087 typedef struct
1089 int i_nb_system_ids;
1090 uint16_t *pi_system_ids;
1092 int i_selected_programs;
1093 int b_high_level;
1094 } system_ids_t;
1096 static bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
1098 int i;
1099 if( p_ids == NULL ) return false;
1100 if( p_ids->b_high_level ) return true;
1102 for ( i = 0; i < p_ids->i_nb_system_ids; i++ )
1103 if ( p_ids->pi_system_ids[i] == i_id )
1104 return true;
1106 return false;
1109 /*****************************************************************************
1110 * CAPMTBuild
1111 *****************************************************************************/
1112 static bool HasCADescriptors( system_ids_t *p_ids, uint8_t *p_descs )
1114 const uint8_t *p_desc;
1115 uint16_t j = 0;
1117 while ( (p_desc = descs_get_desc( p_descs, j )) != NULL )
1119 uint8_t i_tag = desc_get_tag( p_desc );
1120 j++;
1122 if ( i_tag == 0x9 && desc09_validate( p_desc )
1123 && CheckSystemID( p_ids, desc09_get_sysid( p_desc ) ) )
1124 return true;
1127 return false;
1130 static void CopyCADescriptors( system_ids_t *p_ids, uint8_t i_cmd,
1131 uint8_t *p_infos, uint8_t *p_descs )
1133 const uint8_t *p_desc;
1134 uint16_t j = 0, k = 0;
1136 capmti_init( p_infos );
1137 capmti_set_length( p_infos, 0xfff );
1138 capmti_set_cmd( p_infos, i_cmd );
1140 while ( (p_desc = descs_get_desc( p_descs, j )) != NULL )
1142 uint8_t i_tag = desc_get_tag( p_desc );
1143 j++;
1145 if ( i_tag == 0x9 && desc09_validate( p_desc )
1146 && CheckSystemID( p_ids, desc09_get_sysid( p_desc ) ) )
1148 uint8_t *p_info = capmti_get_info( p_infos, k );
1149 k++;
1150 memcpy( p_info, p_desc,
1151 DESC_HEADER_SIZE + desc_get_length( p_desc ) );
1155 if ( k )
1157 uint8_t *p_info = capmti_get_info( p_infos, k );
1158 capmti_set_length( p_infos, p_info - p_infos - DESCS_HEADER_SIZE );
1160 else
1161 capmti_set_length( p_infos, 0 );
1164 static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id,
1165 uint8_t *p_pmt, uint8_t i_list_mgt,
1166 uint8_t i_cmd, int *pi_capmt_size )
1168 system_ids_t *p_ids =
1169 (system_ids_t *)p_sessions[i_session_id - 1].p_sys;
1170 uint8_t *p_es;
1171 uint8_t *p_capmt, *p_capmt_n;
1172 uint16_t j, k;
1173 bool b_has_ca = HasCADescriptors( p_ids, pmt_get_descs( p_pmt ) );
1174 bool b_has_es = false;
1176 j = 0;
1177 while ( (p_es = pmt_get_es( p_pmt, j )) != NULL )
1179 uint16_t i_pid = pmtn_get_pid( p_es );
1180 j++;
1182 if ( demux_PIDIsSelected( i_pid ) )
1184 b_has_es = true;
1185 b_has_ca = b_has_ca
1186 || HasCADescriptors( p_ids, pmtn_get_descs( p_es ) );
1190 if ( !b_has_es )
1192 *pi_capmt_size = 0;
1193 return NULL;
1196 if ( !b_has_ca )
1198 msg_Warn( p_access,
1199 "no compatible scrambling system for SID %d on session %d",
1200 pmt_get_program( p_pmt ), i_session_id );
1201 *pi_capmt_size = 0;
1202 return NULL;
1205 p_capmt = capmt_allocate();
1206 capmt_init( p_capmt );
1207 capmt_set_listmanagement( p_capmt, i_list_mgt );
1208 capmt_set_program( p_capmt, pmt_get_program( p_pmt ) );
1209 capmt_set_version( p_capmt, psi_get_version( p_pmt ) );
1211 CopyCADescriptors( p_ids, i_cmd, capmt_get_infos( p_capmt ),
1212 pmt_get_descs( p_pmt ) );
1214 j = 0; k = 0;
1215 while ( (p_es = pmt_get_es( p_pmt, j )) != NULL )
1217 uint16_t i_pid = pmtn_get_pid( p_es );
1218 j++;
1220 if ( !demux_PIDIsSelected( i_pid ) )
1221 continue;
1223 p_capmt_n = capmt_get_es( p_capmt, k );
1224 k++;
1226 capmtn_init( p_capmt_n );
1227 capmtn_set_streamtype( p_capmt_n, pmtn_get_streamtype( p_es ) );
1228 capmtn_set_pid( p_capmt_n, pmtn_get_pid( p_es ) );
1230 CopyCADescriptors( p_ids, i_cmd, capmtn_get_infos( p_capmt_n ),
1231 pmtn_get_descs( p_es ) );
1234 p_capmt_n = capmt_get_es( p_capmt, k );
1235 *pi_capmt_size = p_capmt_n - p_capmt;
1237 return p_capmt;
1240 /*****************************************************************************
1241 * CAPMTFirst
1242 *****************************************************************************/
1243 static void CAPMTFirst( access_t * p_access, int i_session_id, uint8_t *p_pmt )
1245 uint8_t *p_capmt;
1246 int i_capmt_size;
1248 msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d",
1249 pmt_get_program( p_pmt ), i_session_id );
1251 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1252 0x3 /* only */, 0x1 /* ok_descrambling */,
1253 &i_capmt_size );
1255 if ( i_capmt_size )
1257 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1258 free( p_capmt );
1262 /*****************************************************************************
1263 * CAPMTAdd
1264 *****************************************************************************/
1265 static void CAPMTAdd( access_t * p_access, int i_session_id, uint8_t *p_pmt )
1267 system_ids_t *p_ids =
1268 (system_ids_t *)p_sessions[i_session_id - 1].p_sys;
1269 uint8_t *p_capmt;
1270 int i_capmt_size;
1272 p_ids->i_selected_programs++;
1273 if( p_ids->i_selected_programs == 1 )
1275 CAPMTFirst( p_access, i_session_id, p_pmt );
1276 return;
1279 msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
1280 pmt_get_program( p_pmt ), i_session_id );
1282 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1283 0x4 /* add */, 0x1 /* ok_descrambling */,
1284 &i_capmt_size );
1286 if ( i_capmt_size )
1288 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1289 free( p_capmt );
1293 /*****************************************************************************
1294 * CAPMTUpdate
1295 *****************************************************************************/
1296 static void CAPMTUpdate( access_t * p_access, int i_session_id, uint8_t *p_pmt )
1298 uint8_t *p_capmt;
1299 int i_capmt_size;
1301 msg_Dbg( p_access, "updating CAPMT for SID %d on session %d",
1302 pmt_get_program( p_pmt ), i_session_id );
1304 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1305 0x5 /* update */, 0x1 /* ok_descrambling */,
1306 &i_capmt_size );
1308 if ( i_capmt_size )
1310 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1311 free( p_capmt );
1315 /*****************************************************************************
1316 * CAPMTDelete
1317 *****************************************************************************/
1318 static void CAPMTDelete( access_t * p_access, int i_session_id, uint8_t *p_pmt )
1320 system_ids_t *p_ids =
1321 (system_ids_t *)p_sessions[i_session_id - 1].p_sys;
1322 uint8_t *p_capmt;
1323 int i_capmt_size;
1325 p_ids->i_selected_programs--;
1326 msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d",
1327 pmt_get_program( p_pmt ), i_session_id );
1329 p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1330 0x5 /* update */, 0x4 /* not selected */,
1331 &i_capmt_size );
1333 if ( i_capmt_size )
1335 APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1336 free( p_capmt );
1340 /*****************************************************************************
1341 * ConditionalAccessHandle
1342 *****************************************************************************/
1343 static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
1344 uint8_t *p_apdu, int i_size )
1346 system_ids_t *p_ids =
1347 (system_ids_t *)p_sessions[i_session_id - 1].p_sys;
1348 int i_tag = APDUGetTag( p_apdu, i_size );
1350 switch ( i_tag )
1352 case AOT_CA_INFO:
1354 int i;
1355 int l = 0;
1356 uint8_t *d = APDUGetLength( p_apdu, &l );
1357 msg_Dbg( p_access, "CA system IDs supported by the application :" );
1359 if ( p_ids->i_nb_system_ids )
1360 free( p_ids->pi_system_ids );
1361 p_ids->i_nb_system_ids = l / 2;
1362 p_ids->pi_system_ids = malloc( p_ids->i_nb_system_ids
1363 * sizeof(uint16_t) );
1365 for ( i = 0; i < p_ids->i_nb_system_ids; i++ )
1367 p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
1368 d += 2;
1369 msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] );
1372 demux_ResendCAPMTs();
1373 break;
1376 case AOT_CA_UPDATE:
1377 /* http://www.cablelabs.com/specifications/OC-SP-HOSTPOD-IF-I08-011221.pdf */
1378 case AOT_CA_PMT_REPLY:
1379 /* We do not care */
1380 break;
1382 default:
1383 msg_Err( p_access,
1384 "unexpected tag in ConditionalAccessHandle (0x%x)",
1385 i_tag );
1389 /*****************************************************************************
1390 * ConditionalAccessClose
1391 *****************************************************************************/
1392 static void ConditionalAccessClose( access_t * p_access, int i_session_id )
1394 system_ids_t *p_ids =
1395 (system_ids_t *)p_sessions[i_session_id - 1].p_sys;
1397 msg_Dbg( p_access, "closing ConditionalAccess session (%d)", i_session_id );
1399 if ( p_ids->i_nb_system_ids )
1400 free( p_ids->pi_system_ids );
1402 free( p_sessions[i_session_id - 1].p_sys );
1405 /*****************************************************************************
1406 * ConditionalAccessOpen
1407 *****************************************************************************/
1408 static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
1411 msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
1413 p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
1414 p_sessions[i_session_id - 1].pf_close = ConditionalAccessClose;
1415 p_sessions[i_session_id - 1].p_sys = malloc(sizeof(system_ids_t));
1416 memset( p_sessions[i_session_id - 1].p_sys, 0,
1417 sizeof(system_ids_t) );
1419 APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
1423 * Date Time
1426 typedef struct
1428 int i_session_id;
1429 int i_interval;
1430 struct ev_timer watcher;
1431 } date_time_t;
1433 /*****************************************************************************
1434 * DateTimeSend
1435 *****************************************************************************/
1436 static void DateTimeSend( access_t * p_access, int i_session_id )
1438 date_time_t *p_date =
1439 (date_time_t *)p_sessions[i_session_id - 1].p_sys;
1441 time_t t = time(NULL);
1442 struct tm tm_gmt;
1443 struct tm tm_loc;
1445 if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
1447 int Y = tm_gmt.tm_year;
1448 int M = tm_gmt.tm_mon + 1;
1449 int D = tm_gmt.tm_mday;
1450 int L = (M == 1 || M == 2) ? 1 : 0;
1451 int MJD = 14956 + D + (int)((Y - L) * 365.25)
1452 + (int)((M + 1 + L * 12) * 30.6001);
1453 uint8_t p_response[7];
1455 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
1457 p_response[0] = htons(MJD) >> 8;
1458 p_response[1] = htons(MJD) & 0xff;
1459 p_response[2] = DEC2BCD(tm_gmt.tm_hour);
1460 p_response[3] = DEC2BCD(tm_gmt.tm_min);
1461 p_response[4] = DEC2BCD(tm_gmt.tm_sec);
1462 p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
1463 p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
1465 APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
1467 ev_timer_again(event_loop, &p_date->watcher);
1471 static void _DateTimeSend(struct ev_loop *loop, struct ev_timer *w, int revents)
1473 date_time_t *p_date = container_of(w, date_time_t, watcher);
1474 DateTimeSend( NULL, p_date->i_session_id );
1477 /*****************************************************************************
1478 * DateTimeHandle
1479 *****************************************************************************/
1480 static void DateTimeHandle( access_t * p_access, int i_session_id,
1481 uint8_t *p_apdu, int i_size )
1483 date_time_t *p_date =
1484 (date_time_t *)p_sessions[i_session_id - 1].p_sys;
1486 int i_tag = APDUGetTag( p_apdu, i_size );
1488 switch ( i_tag )
1490 case AOT_DATE_TIME_ENQ:
1492 int l;
1493 const uint8_t *d = APDUGetLength( p_apdu, &l );
1495 if ( l > 0 )
1497 p_date->i_interval = *d;
1498 msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
1499 p_date->i_interval );
1501 else
1502 p_date->i_interval = 0;
1504 ev_timer_stop(event_loop, &p_date->watcher);
1505 ev_timer_set(&p_date->watcher, p_date->i_interval,
1506 p_date->i_interval);
1507 DateTimeSend( p_access, i_session_id );
1508 break;
1510 default:
1511 msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
1515 /*****************************************************************************
1516 * DateTimeClose
1517 *****************************************************************************/
1518 static void DateTimeClose( access_t * p_access, int i_session_id )
1520 date_time_t *p_date =
1521 (date_time_t *)p_sessions[i_session_id - 1].p_sys;
1522 ev_timer_stop(event_loop, &p_date->watcher);
1524 msg_Dbg( p_access, "closing DateTime session (%d)", i_session_id );
1526 free( p_date );
1529 /*****************************************************************************
1530 * DateTimeOpen
1531 *****************************************************************************/
1532 static void DateTimeOpen( access_t * p_access, int i_session_id )
1534 msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
1536 p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
1537 p_sessions[i_session_id - 1].pf_manage = NULL;
1538 p_sessions[i_session_id - 1].pf_close = DateTimeClose;
1539 p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t));
1540 memset( p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
1542 date_time_t *p_date =
1543 (date_time_t *)p_sessions[i_session_id - 1].p_sys;
1544 p_date->i_session_id = i_session_id;
1545 ev_timer_init(&p_date->watcher, _DateTimeSend, 0, 0);
1547 DateTimeSend( p_access, i_session_id );
1551 * MMI
1554 /* Display Control Commands */
1556 #define DCC_SET_MMI_MODE 0x01
1557 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
1558 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
1559 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
1560 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
1562 /* MMI Modes */
1564 #define MM_HIGH_LEVEL 0x01
1565 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
1566 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
1568 /* Display Reply IDs */
1570 #define DRI_MMI_MODE_ACK 0x01
1571 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
1572 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
1573 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
1574 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
1575 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
1576 #define DRI_UNKNOWN_MMI_MODE 0xF1
1577 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
1579 /* Enquiry Flags */
1581 #define EF_BLIND 0x01
1583 /* Answer IDs */
1585 #define AI_CANCEL 0x00
1586 #define AI_ANSWER 0x01
1588 typedef struct
1590 en50221_mmi_object_t last_object;
1591 } mmi_t;
1593 static inline void en50221_MMIFree( en50221_mmi_object_t *p_object )
1595 int i;
1597 switch ( p_object->i_object_type )
1599 case EN50221_MMI_ENQ:
1600 free( p_object->u.enq.psz_text );
1601 break;
1603 case EN50221_MMI_ANSW:
1604 if ( p_object->u.answ.b_ok )
1606 free( p_object->u.answ.psz_answ );
1608 break;
1610 case EN50221_MMI_MENU:
1611 case EN50221_MMI_LIST:
1612 free( p_object->u.menu.psz_title );
1613 free( p_object->u.menu.psz_subtitle );
1614 free( p_object->u.menu.psz_bottom );
1615 for ( i = 0; i < p_object->u.menu.i_choices; i++ )
1617 free( p_object->u.menu.ppsz_choices[i] );
1619 free( p_object->u.menu.ppsz_choices );
1620 break;
1622 default:
1623 break;
1627 /*****************************************************************************
1628 * MMISendObject
1629 *****************************************************************************/
1630 static void MMISendObject( access_t *p_access, int i_session_id,
1631 en50221_mmi_object_t *p_object )
1633 int i_slot = p_sessions[i_session_id - 1].i_slot;
1634 uint8_t *p_data;
1635 int i_size, i_tag;
1637 switch ( p_object->i_object_type )
1639 case EN50221_MMI_ANSW:
1640 i_tag = AOT_ANSW;
1641 i_size = 1 + strlen( p_object->u.answ.psz_answ );
1642 p_data = malloc( i_size );
1643 p_data[0] = (p_object->u.answ.b_ok == true) ? 0x1 : 0x0;
1644 strncpy( (char *)&p_data[1], p_object->u.answ.psz_answ, i_size - 1 );
1645 break;
1647 case EN50221_MMI_MENU_ANSW:
1648 i_tag = AOT_MENU_ANSW;
1649 i_size = 1;
1650 p_data = malloc( i_size );
1651 p_data[0] = p_object->u.menu_answ.i_choice;
1652 break;
1654 default:
1655 msg_Err( p_access, "unknown MMI object %d", p_object->i_object_type );
1656 return;
1659 APDUSend( p_access, i_session_id, i_tag, p_data, i_size );
1660 free( p_data );
1662 p_slots[i_slot].b_mmi_expected = true;
1665 /*****************************************************************************
1666 * MMISendClose
1667 *****************************************************************************/
1668 static void MMISendClose( access_t *p_access, int i_session_id )
1670 int i_slot = p_sessions[i_session_id - 1].i_slot;
1672 APDUSend( p_access, i_session_id, AOT_CLOSE_MMI, NULL, 0 );
1674 p_slots[i_slot].b_mmi_expected = true;
1677 /*****************************************************************************
1678 * MMIDisplayReply
1679 *****************************************************************************/
1680 static void MMIDisplayReply( access_t *p_access, int i_session_id )
1682 uint8_t p_response[2];
1684 p_response[0] = DRI_MMI_MODE_ACK;
1685 p_response[1] = MM_HIGH_LEVEL;
1687 APDUSend( p_access, i_session_id, AOT_DISPLAY_REPLY, p_response, 2 );
1689 msg_Dbg( p_access, "sending DisplayReply on session (%d)", i_session_id );
1692 /*****************************************************************************
1693 * MMIGetText
1694 *****************************************************************************/
1695 static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size )
1697 int i_tag = APDUGetTag( *pp_apdu, *pi_size );
1698 int l;
1699 uint8_t *d;
1701 if ( i_tag != AOT_TEXT_LAST )
1703 msg_Err( p_access, "unexpected text tag: %06x", i_tag );
1704 *pi_size = 0;
1705 return strdup( "" );
1708 d = APDUGetLength( *pp_apdu, &l );
1710 *pp_apdu += l + 4;
1711 *pi_size -= l + 4;
1713 return dvb_string_get( d, l, demux_Iconv, p_access );
1716 /*****************************************************************************
1717 * MMIHandleEnq
1718 *****************************************************************************/
1719 static void MMIHandleEnq( access_t *p_access, int i_session_id,
1720 uint8_t *p_apdu, int i_size )
1722 mmi_t *p_mmi = (mmi_t *)p_sessions[i_session_id - 1].p_sys;
1723 int i_slot = p_sessions[i_session_id - 1].i_slot;
1724 int l;
1725 uint8_t *d = APDUGetLength( p_apdu, &l );
1727 en50221_MMIFree( &p_mmi->last_object );
1728 p_mmi->last_object.i_object_type = EN50221_MMI_ENQ;
1729 p_mmi->last_object.u.enq.b_blind = (*d & 0x1) ? true : false;
1730 d += 2; /* skip answer_text_length because it is not mandatory */
1731 l -= 2;
1732 p_mmi->last_object.u.enq.psz_text = malloc( l + 1 );
1733 strncpy( p_mmi->last_object.u.enq.psz_text, (char *)d, l );
1734 p_mmi->last_object.u.enq.psz_text[l] = '\0';
1736 msg_Dbg( p_access, "MMI enq: %s%s", p_mmi->last_object.u.enq.psz_text,
1737 p_mmi->last_object.u.enq.b_blind == true ? " (blind)" : "" );
1739 p_slots[i_slot].b_mmi_expected = false;
1740 p_slots[i_slot].b_mmi_undisplayed = true;
1743 /*****************************************************************************
1744 * MMIHandleMenu
1745 *****************************************************************************/
1746 static void MMIHandleMenu( access_t *p_access, int i_session_id, int i_tag,
1747 uint8_t *p_apdu, int i_size )
1749 mmi_t *p_mmi = (mmi_t *)p_sessions[i_session_id - 1].p_sys;
1750 int i_slot = p_sessions[i_session_id - 1].i_slot;
1751 int l;
1752 uint8_t *d = APDUGetLength( p_apdu, &l );
1754 en50221_MMIFree( &p_mmi->last_object );
1755 p_mmi->last_object.i_object_type = (i_tag == AOT_MENU_LAST) ?
1756 EN50221_MMI_MENU : EN50221_MMI_LIST;
1757 p_mmi->last_object.u.menu.i_choices = 0;
1758 p_mmi->last_object.u.menu.ppsz_choices = NULL;
1760 if ( l > 0 )
1762 l--; d++; /* choice_nb */
1764 #define GET_FIELD( x ) \
1765 if ( l > 0 ) \
1767 p_mmi->last_object.u.menu.psz_##x \
1768 = MMIGetText( p_access, &d, &l ); \
1769 msg_Dbg( p_access, "MMI " STRINGIFY( x ) ": %s", \
1770 p_mmi->last_object.u.menu.psz_##x ); \
1773 GET_FIELD( title );
1774 GET_FIELD( subtitle );
1775 GET_FIELD( bottom );
1776 #undef GET_FIELD
1778 while ( l > 0 )
1780 char *psz_text = MMIGetText( p_access, &d, &l );
1781 TAB_APPEND( p_mmi->last_object.u.menu.i_choices,
1782 p_mmi->last_object.u.menu.ppsz_choices,
1783 psz_text );
1784 msg_Dbg( p_access, "MMI choice: %s", psz_text );
1788 p_slots[i_slot].b_mmi_expected = false;
1789 p_slots[i_slot].b_mmi_undisplayed = true;
1792 /*****************************************************************************
1793 * MMIHandle
1794 *****************************************************************************/
1795 static void MMIHandle( access_t *p_access, int i_session_id,
1796 uint8_t *p_apdu, int i_size )
1798 int i_tag = APDUGetTag( p_apdu, i_size );
1800 switch ( i_tag )
1802 case AOT_DISPLAY_CONTROL:
1804 int l;
1805 uint8_t *d = APDUGetLength( p_apdu, &l );
1807 if ( l > 0 )
1809 switch ( *d )
1811 case DCC_SET_MMI_MODE:
1812 if ( l == 2 && d[1] == MM_HIGH_LEVEL )
1813 MMIDisplayReply( p_access, i_session_id );
1814 else
1815 msg_Err( p_access, "unsupported MMI mode %02x", d[1] );
1816 break;
1818 default:
1819 msg_Err( p_access, "unsupported display control command %02x",
1820 *d );
1821 break;
1824 break;
1827 case AOT_ENQ:
1828 MMIHandleEnq( p_access, i_session_id, p_apdu, i_size );
1829 break;
1831 case AOT_LIST_LAST:
1832 case AOT_MENU_LAST:
1833 MMIHandleMenu( p_access, i_session_id, i_tag, p_apdu, i_size );
1834 break;
1836 case AOT_CLOSE_MMI:
1837 SessionSendClose( p_access, i_session_id );
1838 break;
1840 default:
1841 msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
1845 /*****************************************************************************
1846 * MMIClose
1847 *****************************************************************************/
1848 static void MMIClose( access_t *p_access, int i_session_id )
1850 int i_slot = p_sessions[i_session_id - 1].i_slot;
1851 mmi_t *p_mmi = (mmi_t *)p_sessions[i_session_id - 1].p_sys;
1853 en50221_MMIFree( &p_mmi->last_object );
1854 free( p_sessions[i_session_id - 1].p_sys );
1856 msg_Dbg( p_access, "closing MMI session (%d)", i_session_id );
1858 p_slots[i_slot].b_mmi_expected = false;
1859 p_slots[i_slot].b_mmi_undisplayed = true;
1862 /*****************************************************************************
1863 * MMIOpen
1864 *****************************************************************************/
1865 static void MMIOpen( access_t *p_access, int i_session_id )
1867 mmi_t *p_mmi;
1869 msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
1871 p_sessions[i_session_id - 1].pf_handle = MMIHandle;
1872 p_sessions[i_session_id - 1].pf_close = MMIClose;
1873 p_sessions[i_session_id - 1].p_sys = malloc(sizeof(mmi_t));
1874 p_mmi = (mmi_t *)p_sessions[i_session_id - 1].p_sys;
1875 p_mmi->last_object.i_object_type = EN50221_MMI_NONE;
1880 * Hardware handling
1883 /*****************************************************************************
1884 * InitSlot: Open the transport layer
1885 *****************************************************************************/
1886 static void InitSlot( access_t * p_access, int i_slot )
1888 if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 ) != 0 )
1889 msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
1890 i_slot );
1893 /*****************************************************************************
1894 * ResetSlot
1895 *****************************************************************************/
1896 static void ResetSlot( int i_slot )
1898 ci_slot_t *p_slot = &p_slots[i_slot];
1899 int i_session_id;
1901 switch (i_print_type)
1903 case PRINT_XML:
1904 fprintf(print_fh, "<STATUS type=\"cam\" status=\"0\" />\n");
1905 break;
1906 case PRINT_TEXT:
1907 fprintf(print_fh, "CAM none\n");
1908 break;
1909 default:
1910 break;
1913 if ( ioctl( i_ca_handle, CA_RESET, 1 << i_slot ) != 0 )
1914 msg_Err( NULL, "en50221_Poll: couldn't reset slot %d", i_slot );
1915 p_slot->b_active = false;
1916 ev_timer_init(&p_slot->init_watcher, ResetSlotCb,
1917 CAM_INIT_TIMEOUT / 1000000., 0);
1918 ev_timer_start(event_loop, &p_slot->init_watcher);
1919 p_slot->b_expect_answer = false;
1920 p_slot->b_mmi_expected = false;
1921 p_slot->b_mmi_undisplayed = false;
1922 if ( p_slot->p_recv != NULL )
1924 free( p_slot->p_recv->p_data );
1925 free( p_slot->p_recv );
1927 p_slot->p_recv = NULL;
1928 while ( p_slot->p_send != NULL )
1930 en50221_msg_t *p_next = p_slot->p_send->p_next;
1931 free( p_slot->p_send->p_data );
1932 free( p_slot->p_send );
1933 p_slot->p_send = p_next;
1935 p_slot->pp_send_last = &p_slot->p_send;
1937 /* Close all sessions for this slot. */
1938 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
1940 if ( p_sessions[i_session_id - 1].i_resource_id
1941 && p_sessions[i_session_id - 1].i_slot == i_slot )
1943 if ( p_sessions[i_session_id - 1].pf_close != NULL )
1945 p_sessions[i_session_id - 1].pf_close( NULL, i_session_id );
1947 p_sessions[i_session_id - 1].i_resource_id = 0;
1952 static void ResetSlotCb(struct ev_loop *loop, struct ev_timer *w, int revents)
1954 ci_slot_t *p_slot = container_of(w, ci_slot_t, init_watcher);
1955 int i_slot = p_slot - &p_slots[0];
1957 if ( p_slot->b_active || !p_slot->b_expect_answer )
1958 return;
1960 msg_Warn( NULL, "no answer from CAM, resetting slot %d",
1961 i_slot );
1962 switch (i_print_type) {
1963 case PRINT_XML:
1964 fprintf(print_fh,
1965 "<EVENT type=\"reset\" cause=\"cam_mute\" />\n");
1966 break;
1967 case PRINT_TEXT:
1968 fprintf(print_fh, "reset cause: cam_mute\n");
1969 break;
1970 default:
1971 break;
1974 ResetSlot( i_slot );
1979 * External entry points
1982 /*****************************************************************************
1983 * en50221_Init : Initialize the CAM for en50221
1984 *****************************************************************************/
1985 void en50221_Init( void )
1987 char psz_tmp[128];
1988 ca_caps_t caps;
1990 memset( &caps, 0, sizeof( ca_caps_t ));
1992 sprintf( psz_tmp, "/dev/dvb/adapter%d/ca%d", i_adapter, i_canum );
1993 if( (i_ca_handle = open(psz_tmp, O_RDWR | O_NONBLOCK)) < 0 )
1995 msg_Warn( NULL, "failed opening CAM device %s (%s)",
1996 psz_tmp, strerror(errno) );
1997 i_ca_handle = 0;
1998 return;
2001 if ( ioctl( i_ca_handle, CA_GET_CAP, &caps ) != 0 )
2003 msg_Err( NULL, "failed getting CAM capabilities (%s)",
2004 strerror(errno) );
2005 close( i_ca_handle );
2006 i_ca_handle = 0;
2007 return;
2010 /* Output CA capabilities */
2011 msg_Dbg( NULL, "CA interface with %d %s", caps.slot_num,
2012 caps.slot_num == 1 ? "slot" : "slots" );
2013 if ( caps.slot_type & CA_CI )
2014 msg_Dbg( NULL, " CI high level interface type" );
2015 if ( caps.slot_type & CA_CI_LINK )
2016 msg_Dbg( NULL, " CI link layer level interface type" );
2017 if ( caps.slot_type & CA_CI_PHYS )
2018 msg_Dbg( NULL, " CI physical layer level interface type (not supported) " );
2019 if ( caps.slot_type & CA_DESCR )
2020 msg_Dbg( NULL, " built-in descrambler detected" );
2021 if ( caps.slot_type & CA_SC )
2022 msg_Dbg( NULL, " simple smart card interface" );
2024 msg_Dbg( NULL, " %d available %s", caps.descr_num,
2025 caps.descr_num == 1 ? "descrambler (key)" : "descramblers (keys)" );
2026 if ( caps.descr_type & CA_ECD )
2027 msg_Dbg( NULL, " ECD scrambling system supported" );
2028 if ( caps.descr_type & CA_NDS )
2029 msg_Dbg( NULL, " NDS scrambling system supported" );
2030 if ( caps.descr_type & CA_DSS )
2031 msg_Dbg( NULL, " DSS scrambling system supported" );
2033 if ( caps.slot_num == 0 )
2035 msg_Err( NULL, "CAM module with no slots" );
2036 close( i_ca_handle );
2037 i_ca_handle = 0;
2038 return;
2041 if( caps.slot_type & CA_CI_LINK )
2042 i_ca_type = CA_CI_LINK;
2043 else if( caps.slot_type & CA_CI )
2044 i_ca_type = CA_CI;
2045 else
2047 msg_Err( NULL, "Incompatible CAM interface" );
2048 close( i_ca_handle );
2049 i_ca_handle = 0;
2050 return;
2053 i_nb_slots = caps.slot_num;
2054 memset( p_sessions, 0, sizeof(en50221_session_t) * MAX_SESSIONS );
2056 if( i_ca_type & CA_CI_LINK )
2058 ev_io_init(&cam_watcher, en50221_Read, i_ca_handle, EV_READ);
2059 ev_io_start(event_loop, &cam_watcher);
2061 ev_timer_init(&slot_watcher, en50221_Poll, CA_POLL_PERIOD / 1000000.,
2062 CA_POLL_PERIOD / 1000000.);
2063 ev_timer_start(event_loop, &slot_watcher);
2066 en50221_Reset();
2069 /*****************************************************************************
2070 * en50221_Reset : Reset the CAM for en50221
2071 *****************************************************************************/
2072 void en50221_Reset( void )
2074 memset( p_slots, 0, sizeof(ci_slot_t) * MAX_CI_SLOTS );
2076 if( i_ca_type & CA_CI_LINK )
2078 int i_slot;
2079 for ( i_slot = 0; i_slot < i_nb_slots; i_slot++ )
2080 ResetSlot( i_slot );
2082 else
2084 struct ca_slot_info info;
2085 system_ids_t *p_ids;
2086 ca_msg_t ca_msg;
2087 info.num = 0;
2089 /* We don't reset the CAM in that case because it's done by the
2090 * ASIC. */
2091 if ( ioctl( i_ca_handle, CA_GET_SLOT_INFO, &info ) < 0 )
2093 msg_Err( NULL, "en50221_Init: couldn't get slot info" );
2094 close( i_ca_handle );
2095 i_ca_handle = 0;
2096 return;
2098 if( info.flags == 0 )
2100 msg_Err( NULL, "en50221_Init: no CAM inserted" );
2101 close( i_ca_handle );
2102 i_ca_handle = 0;
2103 return;
2106 /* Allocate a dummy sessions */
2107 p_sessions[0].i_resource_id = RI_CONDITIONAL_ACCESS_SUPPORT;
2108 p_sessions[0].pf_close = ConditionalAccessClose;
2109 if ( p_sessions[0].p_sys == NULL )
2110 p_sessions[0].p_sys = malloc(sizeof(system_ids_t));
2111 memset( p_sessions[0].p_sys, 0, sizeof(system_ids_t) );
2112 p_ids = (system_ids_t *)p_sessions[0].p_sys;
2113 p_ids->b_high_level = 1;
2115 /* Get application info to find out which cam we are using and make
2116 sure everything is ready to play */
2117 ca_msg.length=3;
2118 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
2119 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
2120 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
2121 memset( &ca_msg.msg[3], 0, 253 );
2122 APDUSend( NULL, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
2123 if ( ioctl( i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
2125 msg_Err( NULL, "en50221_Init: failed getting message" );
2126 close( i_ca_handle );
2127 i_ca_handle = 0;
2128 return;
2131 #ifdef HLCI_WAIT_CAM_READY
2132 while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
2134 msleep(1);
2135 msg_Dbg( NULL, "CAM: please wait" );
2136 APDUSend( NULL, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
2137 ca_msg.length=3;
2138 ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
2139 ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
2140 ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
2141 memset( &ca_msg.msg[3], 0, 253 );
2142 if ( ioctl( i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
2144 msg_Err( NULL, "en50221_Init: failed getting message" );
2145 close( i_ca_handle );
2146 i_ca_handle = 0;
2147 return;
2149 msg_Dbg( NULL, "en50221_Init: Got length: %d, tag: 0x%x", ca_msg.length, APDUGetTag( ca_msg.msg, ca_msg.length ) );
2151 #else
2152 if( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
2154 msg_Err( NULL, "CAM returns garbage as application info!" );
2155 close( i_ca_handle );
2156 i_ca_handle = 0;
2157 return;
2159 #endif
2160 msg_Dbg( NULL, "found CAM %s using id 0x%x", &ca_msg.msg[12],
2161 (ca_msg.msg[8]<<8)|ca_msg.msg[9] );
2165 /*****************************************************************************
2166 * en50221_Read : Read the CAM for a TPDU
2167 *****************************************************************************/
2168 static void en50221_Read(struct ev_loop *loop, struct ev_io *w, int revents)
2170 TPDURecv( NULL );
2172 ev_timer_again(event_loop, &slot_watcher);
2175 /*****************************************************************************
2176 * en50221_Poll : Send a poll TPDU to the CAM
2177 *****************************************************************************/
2178 static void en50221_Poll(struct ev_loop *loop, struct ev_timer *w, int revents)
2180 int i_slot;
2181 int i_session_id;
2183 /* Check module status */
2184 for ( i_slot = 0; i_slot < i_nb_slots; i_slot++ )
2186 ci_slot_t *p_slot = &p_slots[i_slot];
2187 ca_slot_info_t sinfo;
2189 sinfo.num = i_slot;
2190 if ( ioctl( i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
2192 msg_Err( NULL, "en50221_Poll: couldn't get info on slot %d",
2193 i_slot );
2194 continue;
2197 if ( !(sinfo.flags & CA_CI_MODULE_READY) )
2199 if ( p_slot->b_active )
2201 msg_Dbg( NULL, "en50221_Poll: slot %d has been removed",
2202 i_slot );
2203 ResetSlot( i_slot );
2206 else if ( !p_slot->b_active )
2208 if ( !p_slot->b_expect_answer )
2209 InitSlot( NULL, i_slot );
2213 /* Check if applications have data to send */
2214 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2216 en50221_session_t *p_session = &p_sessions[i_session_id - 1];
2217 if ( p_session->i_resource_id && p_session->pf_manage != NULL
2218 && !p_slots[ p_session->i_slot ].b_expect_answer )
2219 p_session->pf_manage( NULL, i_session_id );
2222 /* Now send the poll command to inactive slots */
2223 for ( i_slot = 0; i_slot < i_nb_slots; i_slot++ )
2225 ci_slot_t *p_slot = &p_slots[i_slot];
2227 if ( p_slot->b_active && !p_slot->b_expect_answer )
2229 if ( TPDUSend( NULL, i_slot, T_DATA_LAST, NULL, 0 ) != 0 )
2231 msg_Warn( NULL, "couldn't send TPDU, resetting slot %d",
2232 i_slot );
2233 switch (i_print_type) {
2234 case PRINT_XML:
2235 fprintf(print_fh,
2236 "<EVENT type=\"reset\" cause=\"cam_error\" />\n");
2237 break;
2238 case PRINT_TEXT:
2239 fprintf(print_fh, "reset cause: cam_error\n");
2240 break;
2241 default:
2242 break;
2244 ResetSlot( i_slot );
2250 /*****************************************************************************
2251 * en50221_AddPMT :
2252 *****************************************************************************/
2253 void en50221_AddPMT( uint8_t *p_pmt )
2255 int i_session_id;
2257 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2258 if ( p_sessions[i_session_id - 1].i_resource_id
2259 == RI_CONDITIONAL_ACCESS_SUPPORT )
2260 CAPMTAdd( NULL, i_session_id, p_pmt );
2263 /*****************************************************************************
2264 * en50221_UpdatePMT :
2265 *****************************************************************************/
2266 void en50221_UpdatePMT( uint8_t *p_pmt )
2268 int i_session_id;
2270 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2271 if ( p_sessions[i_session_id - 1].i_resource_id
2272 == RI_CONDITIONAL_ACCESS_SUPPORT )
2273 CAPMTUpdate( NULL, i_session_id, p_pmt );
2276 /*****************************************************************************
2277 * en50221_DeletePMT :
2278 *****************************************************************************/
2279 void en50221_DeletePMT( uint8_t *p_pmt )
2281 int i_session_id;
2283 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2284 if ( p_sessions[i_session_id - 1].i_resource_id
2285 == RI_CONDITIONAL_ACCESS_SUPPORT )
2286 CAPMTDelete( NULL, i_session_id, p_pmt );
2289 /*****************************************************************************
2290 * en50221_StatusMMI :
2291 *****************************************************************************/
2292 uint8_t en50221_StatusMMI( uint8_t *p_answer, ssize_t *pi_size )
2294 struct ret_mmi_status *p_ret = (struct ret_mmi_status *)p_answer;
2296 if ( ioctl( i_ca_handle, CA_GET_CAP, &p_ret->caps ) != 0 )
2298 msg_Err( NULL, "ioctl CA_GET_CAP failed (%s)", strerror(errno) );
2299 return RET_ERR;
2302 *pi_size = sizeof(struct ret_mmi_status);
2303 return RET_MMI_STATUS;
2306 /*****************************************************************************
2307 * en50221_StatusMMISlot :
2308 *****************************************************************************/
2309 uint8_t en50221_StatusMMISlot( uint8_t *p_buffer, ssize_t i_size,
2310 uint8_t *p_answer, ssize_t *pi_size )
2312 int i_slot;
2313 struct ret_mmi_slot_status *p_ret = (struct ret_mmi_slot_status *)p_answer;
2315 if ( i_size != 1 ) return RET_HUH;
2316 i_slot = *p_buffer;
2318 p_ret->sinfo.num = i_slot;
2319 if ( ioctl( i_ca_handle, CA_GET_SLOT_INFO, &p_ret->sinfo ) != 0 )
2321 msg_Err( NULL, "ioctl CA_GET_SLOT_INFO failed (%s)", strerror(errno) );
2322 return RET_ERR;
2325 *pi_size = sizeof(struct ret_mmi_slot_status);
2326 return RET_MMI_SLOT_STATUS;
2329 /*****************************************************************************
2330 * en50221_OpenMMI :
2331 *****************************************************************************/
2332 uint8_t en50221_OpenMMI( uint8_t *p_buffer, ssize_t i_size )
2334 int i_slot;
2336 if ( i_size != 1 ) return RET_HUH;
2337 i_slot = *p_buffer;
2339 if( i_ca_type & CA_CI_LINK )
2341 int i_session_id;
2342 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2344 if ( p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2345 && p_sessions[i_session_id - 1].i_slot == i_slot )
2347 msg_Dbg( NULL,
2348 "MMI menu is already opened on slot %d (session=%d)",
2349 i_slot, i_session_id );
2350 return RET_OK;
2354 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2356 if ( p_sessions[i_session_id - 1].i_resource_id
2357 == RI_APPLICATION_INFORMATION
2358 && p_sessions[i_session_id - 1].i_slot == i_slot )
2360 ApplicationInformationEnterMenu( NULL, i_session_id );
2361 return RET_OK;
2365 msg_Err( NULL, "no application information on slot %d", i_slot );
2366 return RET_ERR;
2368 else
2370 msg_Err( NULL, "MMI menu not supported" );
2371 return RET_ERR;
2375 /*****************************************************************************
2376 * en50221_CloseMMI :
2377 *****************************************************************************/
2378 uint8_t en50221_CloseMMI( uint8_t *p_buffer, ssize_t i_size )
2380 int i_slot;
2382 if ( i_size != 1 ) return RET_HUH;
2383 i_slot = *p_buffer;
2385 if( i_ca_type & CA_CI_LINK )
2387 int i_session_id;
2388 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2390 if ( p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2391 && p_sessions[i_session_id - 1].i_slot == i_slot )
2393 MMISendClose( NULL, i_session_id );
2394 return RET_OK;
2398 msg_Warn( NULL, "closing a non-existing MMI session on slot %d",
2399 i_slot );
2400 return RET_ERR;
2402 else
2404 msg_Err( NULL, "MMI menu not supported" );
2405 return RET_ERR;
2409 /*****************************************************************************
2410 * en50221_GetMMIObject :
2411 *****************************************************************************/
2412 uint8_t en50221_GetMMIObject( uint8_t *p_buffer, ssize_t i_size,
2413 uint8_t *p_answer, ssize_t *pi_size )
2415 int i_session_id, i_slot;
2416 struct ret_mmi_recv *p_ret = (struct ret_mmi_recv *)p_answer;
2418 if ( i_size != 1 ) return RET_HUH;
2419 i_slot = *p_buffer;
2421 if ( p_slots[i_slot].b_mmi_expected )
2422 return RET_MMI_WAIT; /* data not yet available */
2424 p_ret->object.i_object_type = EN50221_MMI_NONE;
2425 *pi_size = sizeof(struct ret_mmi_recv);
2427 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2429 if ( p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2430 && p_sessions[i_session_id - 1].i_slot == i_slot )
2432 mmi_t *p_mmi =
2433 (mmi_t *)p_sessions[i_session_id - 1].p_sys;
2434 if ( p_mmi == NULL )
2436 *pi_size = 0;
2437 return RET_ERR; /* should not happen */
2440 *pi_size = COMM_BUFFER_SIZE - COMM_HEADER_SIZE -
2441 ((void *)&p_ret->object - (void *)p_ret);
2442 if ( en50221_SerializeMMIObject( (uint8_t *)&p_ret->object,
2443 pi_size, &p_mmi->last_object ) == -1 )
2445 *pi_size = 0;
2446 msg_Err( NULL, "MMI structure too big" );
2447 return RET_ERR;
2449 *pi_size += ((void *)&p_ret->object - (void *)p_ret);
2450 break;
2454 return RET_MMI_RECV;
2458 /*****************************************************************************
2459 * en50221_SendMMIObject :
2460 *****************************************************************************/
2461 uint8_t en50221_SendMMIObject( uint8_t *p_buffer, ssize_t i_size )
2463 int i_session_id, i_slot;
2464 struct cmd_mmi_send *p_cmd = (struct cmd_mmi_send *)p_buffer;
2466 if ( i_size < sizeof(struct cmd_mmi_send))
2468 msg_Err( NULL, "command packet too short (%zd)\n", i_size );
2469 return RET_HUH;
2472 if ( en50221_UnserializeMMIObject( &p_cmd->object, i_size -
2473 ((void *)&p_cmd->object - (void *)p_cmd) ) == -1 )
2474 return RET_ERR;
2476 i_slot = p_cmd->i_slot;
2478 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2480 if ( p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2481 && p_sessions[i_session_id - 1].i_slot == i_slot )
2483 MMISendObject( NULL, i_session_id, &p_cmd->object );
2484 return RET_OK;
2488 msg_Err( NULL, "SendMMIObject when no MMI session is opened !" );
2489 return RET_ERR;
2492 #else
2493 #include <inttypes.h>
2495 int i_ca_handle = 0;
2496 int i_ca_type = -1;
2498 void en50221_AddPMT( uint8_t *p_pmt ) { };
2499 void en50221_UpdatePMT( uint8_t *p_pmt ) { };
2500 void en50221_DeletePMT( uint8_t *p_pmt ) { };
2501 void en50221_Reset( void ) { };
2502 #endif