File: | en50221.c |
Location: | line 1762, column 13 |
Description: | Result of 'malloc' is converted to a pointer of type 'char *', which is incompatible with sizeof operand type 'void **' |
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 | * |
7 | * Authors: Christophe Massiot <massiot@via.ecp.fr> |
8 | * Based on code from libdvbci Copyright (C) 2000 Klaus Schmidinger |
9 | * |
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. |
14 | * |
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. |
19 | * |
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" |
25 | |
26 | #ifdef HAVE_DVB_SUPPORT |
27 | |
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(*__errno_location ()).h> |
45 | #include <time.h> |
46 | |
47 | /* DVB Card Drivers */ |
48 | #include <linux1/dvb/version.h> |
49 | #include <linux1/dvb/dmx.h> |
50 | #include <linux1/dvb/frontend.h> |
51 | #include <linux1/dvb/ca.h> |
52 | |
53 | #include <bitstream/mpeg/psi.h> |
54 | #include <bitstream/dvb/ci.h> |
55 | #include <bitstream/dvb/si.h> |
56 | |
57 | #include "dvblast.h" |
58 | #include "en50221.h" |
59 | #include "comm.h" |
60 | |
61 | #define TAB_APPEND( count, tab, p )if( (count) > 0 ) { (tab) = realloc( tab, sizeof( void ** ) * ( (count) + 1 ) ); } else { (tab) = malloc( sizeof( void * * ) ); } (tab)[count] = (p); (count)++ \ |
62 | if( (count) > 0 ) \ |
63 | { \ |
64 | (tab) = realloc( tab, sizeof( void ** ) * ( (count) + 1 ) ); \ |
65 | } \ |
66 | else \ |
67 | { \ |
68 | (tab) = malloc( sizeof( void ** ) ); \ |
69 | } \ |
70 | (tab)[count] = (p); \ |
71 | (count)++ |
72 | |
73 | /***************************************************************************** |
74 | * Local declarations |
75 | *****************************************************************************/ |
76 | #undef DEBUG_TPDU |
77 | #define CAM_INIT_TIMEOUT15000000 15000000 /* 15 s */ |
78 | #undef HLCI_WAIT_CAM_READY |
79 | #define CAPMT_WAIT100 100 /* ms */ |
80 | |
81 | typedef struct en50221_msg_t |
82 | { |
83 | uint8_t *p_data; |
84 | int i_size; |
85 | struct en50221_msg_t *p_next; |
86 | } en50221_msg_t; |
87 | |
88 | typedef struct en50221_session_t |
89 | { |
90 | int i_slot; |
91 | int i_resource_id; |
92 | void (* pf_handle)( access_t *, int, uint8_t *, int ); |
93 | void (* pf_close)( access_t *, int ); |
94 | void (* pf_manage)( access_t *, int ); |
95 | void *p_sys; |
96 | } en50221_session_t; |
97 | |
98 | typedef struct ci_slot_t |
99 | { |
100 | bool_Bool b_active; |
101 | bool_Bool b_expect_answer; |
102 | bool_Bool b_has_data; |
103 | bool_Bool b_mmi_expected; |
104 | bool_Bool b_mmi_undisplayed; |
105 | |
106 | /* TPDU reception */ |
107 | en50221_msg_t *p_recv; |
108 | |
109 | /* TPDU emission */ |
110 | en50221_msg_t *p_send; |
111 | en50221_msg_t **pp_send_last; |
112 | uint8_t *p; |
113 | |
114 | /* InitSlot callback, if not 0 */ |
115 | mtime_t i_init_timeout; |
116 | |
117 | /* SPDUSend callback, if p_spdu is not NULL */ |
118 | /* SessionOpen callback, if not 0 */ |
119 | int i_pending_session_id; |
120 | } ci_slot_t; |
121 | |
122 | int i_ca_handle = 0; |
123 | int i_ca_type = -1; |
124 | |
125 | static int i_nb_slots = 0; |
126 | static ci_slot_t p_slots[MAX_CI_SLOTS16]; |
127 | static en50221_session_t p_sessions[MAX_SESSIONS32]; |
128 | |
129 | /***************************************************************************** |
130 | * Local prototypes |
131 | *****************************************************************************/ |
132 | static void SessionOpenCb( access_t *p_access, uint8_t i_slot ); |
133 | static void SPDUHandle( access_t * p_access, uint8_t i_slot, |
134 | uint8_t *p_spdu, int i_size ); |
135 | |
136 | static void ResourceManagerOpen( access_t * p_access, int i_session_id ); |
137 | static void ApplicationInformationOpen( access_t * p_access, int i_session_id ); |
138 | static void ConditionalAccessOpen( access_t * p_access, int i_session_id ); |
139 | static void DateTimeOpen( access_t * p_access, int i_session_id ); |
140 | static void MMIOpen( access_t * p_access, int i_session_id ); |
141 | |
142 | /***************************************************************************** |
143 | * Utility functions |
144 | *****************************************************************************/ |
145 | #define SIZE_INDICATOR0x80 0x80 |
146 | |
147 | static uint8_t *GetLength( uint8_t *p_data, int *pi_length ) |
148 | { |
149 | *pi_length = *p_data++; |
150 | |
151 | if ( (*pi_length & SIZE_INDICATOR0x80) != 0 ) |
152 | { |
153 | int l = *pi_length & ~SIZE_INDICATOR0x80; |
154 | int i; |
155 | |
156 | *pi_length = 0; |
157 | for ( i = 0; i < l; i++ ) |
158 | *pi_length = (*pi_length << 8) | *p_data++; |
159 | } |
160 | |
161 | return p_data; |
162 | } |
163 | |
164 | static uint8_t *SetLength( uint8_t *p_data, int i_length ) |
165 | { |
166 | uint8_t *p = p_data; |
167 | |
168 | if ( i_length < 128 ) |
169 | { |
170 | *p++ = i_length; |
171 | } |
172 | else if ( i_length < 256 ) |
173 | { |
174 | *p++ = SIZE_INDICATOR0x80 | 0x1; |
175 | *p++ = i_length; |
176 | } |
177 | else if ( i_length < 65536 ) |
178 | { |
179 | *p++ = SIZE_INDICATOR0x80 | 0x2; |
180 | *p++ = i_length >> 8; |
181 | *p++ = i_length & 0xff; |
182 | } |
183 | else if ( i_length < 16777216 ) |
184 | { |
185 | *p++ = SIZE_INDICATOR0x80 | 0x3; |
186 | *p++ = i_length >> 16; |
187 | *p++ = (i_length >> 8) & 0xff; |
188 | *p++ = i_length & 0xff; |
189 | } |
190 | else |
191 | { |
192 | *p++ = SIZE_INDICATOR0x80 | 0x4; |
193 | *p++ = i_length >> 24; |
194 | *p++ = (i_length >> 16) & 0xff; |
195 | *p++ = (i_length >> 8) & 0xff; |
196 | *p++ = i_length & 0xff; |
197 | } |
198 | |
199 | return p; |
200 | } |
201 | |
202 | |
203 | /* |
204 | * Transport layer |
205 | */ |
206 | |
207 | #define MAX_TPDU_SIZE4096 4096 |
208 | #define MAX_TPDU_DATA(4096 - 7) (MAX_TPDU_SIZE4096 - 7) |
209 | |
210 | #define DATA_INDICATOR0x80 0x80 |
211 | |
212 | #define T_SB0x80 0x80 |
213 | #define T_RCV0x81 0x81 |
214 | #define T_CREATE_TC0x82 0x82 |
215 | #define T_CTC_REPLY0x83 0x83 |
216 | #define T_DELETE_TC0x84 0x84 |
217 | #define T_DTC_REPLY0x85 0x85 |
218 | #define T_REQUEST_TC0x86 0x86 |
219 | #define T_NEW_TC0x87 0x87 |
220 | #define T_TC_ERROR0x88 0x88 |
221 | #define T_DATA_LAST0xA0 0xA0 |
222 | #define T_DATA_MORE0xA1 0xA1 |
223 | |
224 | static void Dump( bool_Bool b_outgoing, uint8_t *p_data, int i_size ) |
225 | { |
226 | #ifdef DEBUG_TPDU |
227 | int i; |
228 | #define MAX_DUMP 256 |
229 | fprintf(stderrstderr, "%s ", b_outgoing ? "-->" : "<--"); |
230 | for ( i = 0; i < i_size && i < MAX_DUMP; i++) |
231 | fprintf(stderrstderr, "%02X ", p_data[i]); |
232 | fprintf(stderrstderr, "%s\n", i_size >= MAX_DUMP ? "..." : ""); |
233 | #endif |
234 | } |
235 | |
236 | /***************************************************************************** |
237 | * TPDUWrite |
238 | *****************************************************************************/ |
239 | static int TPDUWrite( access_t * p_access, uint8_t i_slot ) |
240 | { |
241 | ci_slot_t *p_slot = &p_slots[i_slot]; |
242 | en50221_msg_t *p_send = p_slot->p_send; |
243 | |
244 | if ( p_slot->b_expect_answer ) |
245 | msg_Warn( p_access, |
246 | "en50221: writing while expecting an answer on slot %u", |
247 | i_slot ); |
248 | if ( p_send == NULL((void*)0) ) |
249 | { |
250 | msg_Warn( p_access, "en50221: no data to write on slot %u !", i_slot ); |
251 | return -1; |
252 | } |
253 | p_slot->p_send = p_send->p_next; |
254 | if ( p_slot->p_send == NULL((void*)0) ) |
255 | p_slot->pp_send_last = &p_slot->p_send; |
256 | |
257 | Dump( true1, p_send->p_data, p_send->i_size ); |
258 | |
259 | if ( write( i_ca_handle, p_send->p_data, p_send->i_size ) |
260 | != p_send->i_size ) |
261 | { |
262 | msg_Err( p_access, "en50221: cannot write to CAM device (%m)" ); |
263 | free( p_send->p_data ); |
264 | free( p_send ); |
265 | return -1; |
266 | } |
267 | |
268 | free( p_send->p_data ); |
269 | free( p_send ); |
270 | p_slot->b_expect_answer = true1; |
271 | |
272 | return 0; |
273 | } |
274 | |
275 | /***************************************************************************** |
276 | * TPDUSend |
277 | *****************************************************************************/ |
278 | static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag, |
279 | const uint8_t *p_content, int i_length ) |
280 | { |
281 | ci_slot_t *p_slot = &p_slots[i_slot]; |
282 | uint8_t i_tcid = i_slot + 1; |
283 | en50221_msg_t *p_send = malloc( sizeof(en50221_msg_t) ); |
284 | uint8_t *p_data = malloc( MAX_TPDU_SIZE4096 ); |
285 | int i_size; |
286 | |
287 | i_size = 0; |
288 | p_data[0] = i_slot; |
289 | p_data[1] = i_tcid; |
290 | p_data[2] = i_tag; |
291 | |
292 | switch ( i_tag ) |
293 | { |
294 | case T_RCV0x81: |
295 | case T_CREATE_TC0x82: |
296 | case T_CTC_REPLY0x83: |
297 | case T_DELETE_TC0x84: |
298 | case T_DTC_REPLY0x85: |
299 | case T_REQUEST_TC0x86: |
300 | p_data[3] = 1; /* length */ |
301 | p_data[4] = i_tcid; |
302 | i_size = 5; |
303 | break; |
304 | |
305 | case T_NEW_TC0x87: |
306 | case T_TC_ERROR0x88: |
307 | p_data[3] = 2; /* length */ |
308 | p_data[4] = i_tcid; |
309 | p_data[5] = p_content[0]; |
310 | i_size = 6; |
311 | break; |
312 | |
313 | case T_DATA_LAST0xA0: |
314 | case T_DATA_MORE0xA1: |
315 | { |
316 | /* i_length <= MAX_TPDU_DATA */ |
317 | uint8_t *p = p_data + 3; |
318 | p = SetLength( p, i_length + 1 ); |
319 | *p++ = i_tcid; |
320 | |
321 | if ( i_length ) |
322 | memcpy( p, p_content, i_length ); |
323 | i_size = i_length + (p - p_data); |
324 | break; |
325 | } |
326 | |
327 | default: |
328 | break; |
329 | } |
330 | |
331 | p_send->p_data = p_data; |
332 | p_send->i_size = i_size; |
333 | p_send->p_next = NULL((void*)0); |
334 | |
335 | *p_slot->pp_send_last = p_send; |
336 | p_slot->pp_send_last = &p_send->p_next; |
337 | |
338 | if ( !p_slot->b_expect_answer ) |
339 | return TPDUWrite( p_access, i_slot ); |
340 | |
341 | return 0; |
342 | } |
343 | |
344 | |
345 | /***************************************************************************** |
346 | * TPDURecv |
347 | *****************************************************************************/ |
348 | static int TPDURecv( access_t * p_access ) |
349 | { |
350 | ci_slot_t *p_slot; |
351 | uint8_t i_tag, i_slot; |
352 | uint8_t p_data[MAX_TPDU_SIZE4096]; |
353 | int i_size; |
354 | bool_Bool b_last = false0; |
355 | |
356 | do |
357 | { |
358 | i_size = read( i_ca_handle, p_data, MAX_TPDU_SIZE4096 ); |
359 | } |
360 | while ( i_size < 0 && errno(*__errno_location ()) == EINTR4 ); |
361 | |
362 | if ( i_size < 5 ) |
363 | { |
364 | msg_Err( p_access, "en50221: cannot read from CAM device (%d:%m)", |
365 | i_size ); |
366 | return -1; |
367 | } |
368 | |
369 | Dump( false0, p_data, i_size ); |
370 | |
371 | i_slot = p_data[1] - 1; |
372 | i_tag = p_data[2]; |
373 | |
374 | if ( i_slot >= i_nb_slots ) |
375 | { |
376 | msg_Warn( p_access, "en50221: TPDU is from an unknown slot %u", |
377 | i_slot ); |
378 | return -1; |
379 | } |
380 | p_slot = &p_slots[i_slot]; |
381 | |
382 | p_slot->b_has_data = !!(p_data[i_size - 4] == T_SB0x80 |
383 | && p_data[i_size - 3] == 2 |
384 | && (p_data[i_size - 1] & DATA_INDICATOR0x80)); |
385 | p_slot->b_expect_answer = false0; |
386 | |
387 | switch ( i_tag ) |
388 | { |
389 | case T_CTC_REPLY0x83: |
390 | p_slot->b_active = true1; |
391 | p_slot->i_init_timeout = 0; |
392 | msg_Dbg( p_access, "CI slot %d is active", i_slot ); |
393 | break; |
394 | |
395 | case T_SB0x80: |
396 | break; |
397 | |
398 | case T_DATA_LAST0xA0: |
399 | b_last = true1; |
400 | /* intended pass-through */ |
401 | case T_DATA_MORE0xA1: |
402 | { |
403 | en50221_msg_t *p_recv; |
404 | int i_session_size; |
405 | uint8_t *p_session = GetLength( &p_data[3], &i_session_size ); |
406 | |
407 | if ( i_session_size <= 1 ) |
408 | break; |
409 | p_session++; |
410 | i_session_size--; |
411 | |
412 | if ( p_slot->p_recv == NULL((void*)0) ) |
413 | { |
414 | p_slot->p_recv = malloc( sizeof(en50221_msg_t) ); |
415 | p_slot->p_recv->p_data = NULL((void*)0); |
416 | p_slot->p_recv->i_size = 0; |
417 | } |
418 | |
419 | p_recv = p_slot->p_recv; |
420 | p_recv->p_data = realloc( p_recv->p_data, |
421 | p_recv->i_size + i_session_size ); |
422 | memcpy( &p_recv->p_data[ p_recv->i_size ], p_session, i_session_size ); |
423 | p_recv->i_size += i_session_size; |
424 | |
425 | if ( b_last ) |
426 | { |
427 | SPDUHandle( p_access, i_slot, p_recv->p_data, p_recv->i_size ); |
428 | free( p_recv->p_data ); |
429 | free( p_recv ); |
430 | p_slot->p_recv = NULL((void*)0); |
431 | } |
432 | break; |
433 | } |
434 | |
435 | default: |
436 | msg_Warn( p_access, "en50221: unhandled R_TPDU tag %u slot %u", i_tag, |
437 | i_slot ); |
438 | break; |
439 | } |
440 | |
441 | if ( !p_slot->b_expect_answer && p_slot->p_send != NULL((void*)0) ) |
442 | TPDUWrite( p_access, i_slot ); |
443 | if ( !p_slot->b_expect_answer && p_slot->i_pending_session_id != 0 ) |
444 | SessionOpenCb( p_access, i_slot ); |
445 | if ( !p_slot->b_expect_answer && p_slot->b_has_data ) |
446 | TPDUSend( p_access, i_slot, T_RCV0x81, NULL((void*)0), 0 ); |
447 | |
448 | return 0; |
449 | } |
450 | |
451 | |
452 | /* |
453 | * Session layer |
454 | */ |
455 | |
456 | #define ST_SESSION_NUMBER0x90 0x90 |
457 | #define ST_OPEN_SESSION_REQUEST0x91 0x91 |
458 | #define ST_OPEN_SESSION_RESPONSE0x92 0x92 |
459 | #define ST_CREATE_SESSION0x93 0x93 |
460 | #define ST_CREATE_SESSION_RESPONSE0x94 0x94 |
461 | #define ST_CLOSE_SESSION_REQUEST0x95 0x95 |
462 | #define ST_CLOSE_SESSION_RESPONSE0x96 0x96 |
463 | |
464 | #define SS_OK0x00 0x00 |
465 | #define SS_NOT_ALLOCATED0xF0 0xF0 |
466 | |
467 | #define RI_RESOURCE_MANAGER0x00010041 0x00010041 |
468 | #define RI_APPLICATION_INFORMATION0x00020041 0x00020041 |
469 | #define RI_CONDITIONAL_ACCESS_SUPPORT0x00030041 0x00030041 |
470 | #define RI_HOST_CONTROL0x00200041 0x00200041 |
471 | #define RI_DATE_TIME0x00240041 0x00240041 |
472 | #define RI_MMI0x00400041 0x00400041 |
473 | |
474 | static int ResourceIdToInt( uint8_t *p_data ) |
475 | { |
476 | return ((int)p_data[0] << 24) | ((int)p_data[1] << 16) |
477 | | ((int)p_data[2] << 8) | p_data[3]; |
478 | } |
479 | |
480 | /***************************************************************************** |
481 | * SPDUSend |
482 | *****************************************************************************/ |
483 | static int SPDUSend( access_t *p_access, int i_session_id, |
484 | uint8_t *p_data, int i_size ) |
485 | { |
486 | uint8_t *p_spdu = malloc( i_size + 4 ); |
487 | uint8_t *p = p_spdu; |
488 | uint8_t i_slot = p_sessions[i_session_id - 1].i_slot; |
489 | |
490 | *p++ = ST_SESSION_NUMBER0x90; |
491 | *p++ = 0x02; |
492 | *p++ = (i_session_id >> 8); |
493 | *p++ = i_session_id & 0xff; |
494 | |
495 | memcpy( p, p_data, i_size ); |
496 | |
497 | i_size += 4; |
498 | p = p_spdu; |
499 | |
500 | while ( i_size > 0 ) |
501 | { |
502 | if ( i_size > MAX_TPDU_DATA(4096 - 7) ) |
503 | { |
504 | if ( TPDUSend( p_access, i_slot, T_DATA_MORE0xA1, p, |
505 | MAX_TPDU_DATA(4096 - 7) ) != 0 ) |
506 | { |
507 | msg_Err( p_access, "couldn't send TPDU on session %d", |
508 | i_session_id ); |
509 | free( p_spdu ); |
510 | return -1; |
511 | } |
512 | p += MAX_TPDU_DATA(4096 - 7); |
513 | i_size -= MAX_TPDU_DATA(4096 - 7); |
514 | } |
515 | else |
516 | { |
517 | if ( TPDUSend( p_access, i_slot, T_DATA_LAST0xA0, p, i_size ) |
518 | != 0 ) |
519 | { |
520 | msg_Err( p_access, "couldn't send TPDU on session %d", |
521 | i_session_id ); |
522 | free( p_spdu ); |
523 | return -1; |
524 | } |
525 | i_size = 0; |
526 | } |
527 | } |
528 | |
529 | free( p_spdu ); |
530 | return 0; |
531 | } |
532 | |
533 | /***************************************************************************** |
534 | * SessionOpen |
535 | *****************************************************************************/ |
536 | static void SessionOpenCb( access_t *p_access, uint8_t i_slot ) |
537 | { |
538 | ci_slot_t *p_slot = &p_slots[i_slot]; |
539 | int i_session_id = p_slot->i_pending_session_id; |
540 | int i_resource_id = p_sessions[i_session_id - 1].i_resource_id; |
541 | |
542 | p_slot->i_pending_session_id = 0; |
543 | |
544 | switch ( i_resource_id ) |
545 | { |
546 | case RI_RESOURCE_MANAGER0x00010041: |
547 | ResourceManagerOpen( p_access, i_session_id ); break; |
548 | case RI_APPLICATION_INFORMATION0x00020041: |
549 | ApplicationInformationOpen( p_access, i_session_id ); break; |
550 | case RI_CONDITIONAL_ACCESS_SUPPORT0x00030041: |
551 | ConditionalAccessOpen( p_access, i_session_id ); break; |
552 | case RI_DATE_TIME0x00240041: |
553 | DateTimeOpen( p_access, i_session_id ); break; |
554 | case RI_MMI0x00400041: |
555 | MMIOpen( p_access, i_session_id ); break; |
556 | |
557 | case RI_HOST_CONTROL0x00200041: |
558 | default: |
559 | msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id ); |
560 | p_sessions[i_session_id - 1].i_resource_id = 0; |
561 | } |
562 | } |
563 | |
564 | static void SessionOpen( access_t * p_access, uint8_t i_slot, |
565 | uint8_t *p_spdu, int i_size ) |
566 | { |
567 | ci_slot_t *p_slot = &p_slots[i_slot]; |
568 | int i_session_id; |
569 | int i_resource_id = ResourceIdToInt( &p_spdu[2] ); |
570 | uint8_t p_response[16]; |
571 | int i_status = SS_NOT_ALLOCATED0xF0; |
572 | |
573 | for ( i_session_id = 1; i_session_id <= MAX_SESSIONS32; i_session_id++ ) |
574 | { |
575 | if ( !p_sessions[i_session_id - 1].i_resource_id ) |
576 | break; |
577 | } |
578 | if ( i_session_id > MAX_SESSIONS32 ) |
579 | { |
580 | msg_Err( p_access, "too many sessions !" ); |
581 | return; |
582 | } |
583 | p_sessions[i_session_id - 1].i_slot = i_slot; |
584 | p_sessions[i_session_id - 1].i_resource_id = i_resource_id; |
585 | p_sessions[i_session_id - 1].pf_close = NULL((void*)0); |
586 | p_sessions[i_session_id - 1].pf_manage = NULL((void*)0); |
587 | |
588 | if ( i_resource_id == RI_RESOURCE_MANAGER0x00010041 |
589 | || i_resource_id == RI_APPLICATION_INFORMATION0x00020041 |
590 | || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT0x00030041 |
591 | || i_resource_id == RI_DATE_TIME0x00240041 |
592 | || i_resource_id == RI_MMI0x00400041 ) |
593 | { |
594 | i_status = SS_OK0x00; |
595 | } |
596 | |
597 | p_response[0] = ST_OPEN_SESSION_RESPONSE0x92; |
598 | p_response[1] = 0x7; |
599 | p_response[2] = i_status; |
600 | p_response[3] = p_spdu[2]; |
601 | p_response[4] = p_spdu[3]; |
602 | p_response[5] = p_spdu[4]; |
603 | p_response[6] = p_spdu[5]; |
604 | p_response[7] = i_session_id >> 8; |
605 | p_response[8] = i_session_id & 0xff; |
606 | |
607 | if ( TPDUSend( p_access, i_slot, T_DATA_LAST0xA0, p_response, 9 ) != 0 ) |
608 | { |
609 | msg_Err( p_access, |
610 | "SessionOpen: couldn't send TPDU on slot %d", i_slot ); |
611 | return; |
612 | } |
613 | |
614 | if ( p_slot->i_pending_session_id != 0 ) |
615 | msg_Warn( p_access, "overwriting pending session %d", |
616 | p_slot->i_pending_session_id ); |
617 | p_slot->i_pending_session_id = i_session_id; |
618 | } |
619 | |
620 | #if 0 |
621 | /* unused code for the moment - commented out to keep gcc happy */ |
622 | /***************************************************************************** |
623 | * SessionCreate |
624 | *****************************************************************************/ |
625 | static void SessionCreate( access_t * p_access, int i_slot, int i_resource_id ) |
626 | { |
627 | uint8_t p_response[16]; |
628 | uint8_t i_tag; |
629 | int i_session_id; |
630 | |
631 | for ( i_session_id = 1; i_session_id <= MAX_SESSIONS32; i_session_id++ ) |
632 | { |
633 | if ( !p_sessions[i_session_id - 1].i_resource_id ) |
634 | break; |
635 | } |
636 | if ( i_session_id > MAX_SESSIONS32 ) |
637 | { |
638 | msg_Err( p_access, "too many sessions !" ); |
639 | return; |
640 | } |
641 | p_sessions[i_session_id - 1].i_slot = i_slot; |
642 | p_sessions[i_session_id - 1].i_resource_id = i_resource_id; |
643 | p_sessions[i_session_id - 1].pf_close = NULL((void*)0); |
644 | p_sessions[i_session_id - 1].pf_manage = NULL((void*)0); |
645 | p_sessions[i_session_id - 1].p_sys = NULL((void*)0); |
646 | |
647 | p_response[0] = ST_CREATE_SESSION0x93; |
648 | p_response[1] = 0x6; |
649 | p_response[2] = i_resource_id >> 24; |
650 | p_response[3] = (i_resource_id >> 16) & 0xff; |
651 | p_response[4] = (i_resource_id >> 8) & 0xff; |
652 | p_response[5] = i_resource_id & 0xff; |
653 | p_response[6] = i_session_id >> 8; |
654 | p_response[7] = i_session_id & 0xff; |
655 | |
656 | if ( TPDUSend( p_access, i_slot, T_DATA_LAST0xA0, p_response, 4 ) != |
657 | 0 ) |
658 | { |
659 | msg_Err( p_access, |
660 | "SessionCreate: couldn't send TPDU on slot %d", i_slot ); |
661 | return; |
662 | } |
663 | } |
664 | #endif |
665 | |
666 | /***************************************************************************** |
667 | * SessionCreateResponse |
668 | *****************************************************************************/ |
669 | static void SessionCreateResponse( access_t * p_access, uint8_t i_slot, |
670 | uint8_t *p_spdu, int i_size ) |
671 | { |
672 | int i_status = p_spdu[2]; |
673 | int i_resource_id = ResourceIdToInt( &p_spdu[3] ); |
674 | int i_session_id = ((int)p_spdu[7] << 8) | p_spdu[8]; |
675 | |
676 | if ( i_status != SS_OK0x00 ) |
677 | { |
678 | msg_Err( p_access, "SessionCreateResponse: failed to open session %d" |
679 | " resource=0x%x status=0x%x", i_session_id, i_resource_id, |
680 | i_status ); |
681 | p_sessions[i_session_id - 1].i_resource_id = 0; |
682 | return; |
683 | } |
684 | |
685 | switch ( i_resource_id ) |
686 | { |
687 | case RI_RESOURCE_MANAGER0x00010041: |
688 | ResourceManagerOpen( p_access, i_session_id ); break; |
689 | case RI_APPLICATION_INFORMATION0x00020041: |
690 | ApplicationInformationOpen( p_access, i_session_id ); break; |
691 | case RI_CONDITIONAL_ACCESS_SUPPORT0x00030041: |
692 | ConditionalAccessOpen( p_access, i_session_id ); break; |
693 | case RI_DATE_TIME0x00240041: |
694 | DateTimeOpen( p_access, i_session_id ); break; |
695 | case RI_MMI0x00400041: |
696 | MMIOpen( p_access, i_session_id ); break; |
697 | |
698 | case RI_HOST_CONTROL0x00200041: |
699 | default: |
700 | msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id ); |
701 | p_sessions[i_session_id - 1].i_resource_id = 0; |
702 | } |
703 | } |
704 | |
705 | /***************************************************************************** |
706 | * SessionSendClose |
707 | *****************************************************************************/ |
708 | static void SessionSendClose( access_t * p_access, int i_session_id ) |
709 | { |
710 | uint8_t p_response[16]; |
711 | uint8_t i_slot = p_sessions[i_session_id - 1].i_slot; |
712 | |
713 | p_response[0] = ST_CLOSE_SESSION_REQUEST0x95; |
714 | p_response[1] = 0x2; |
715 | p_response[2] = i_session_id >> 8; |
716 | p_response[3] = i_session_id & 0xff; |
717 | |
718 | if ( TPDUSend( p_access, i_slot, T_DATA_LAST0xA0, p_response, 4 ) != |
719 | 0 ) |
720 | { |
721 | msg_Err( p_access, |
722 | "SessionSendClose: couldn't send TPDU on slot %d", i_slot ); |
723 | return; |
724 | } |
725 | } |
726 | |
727 | /***************************************************************************** |
728 | * SessionClose |
729 | *****************************************************************************/ |
730 | static void SessionClose( access_t * p_access, int i_session_id ) |
731 | { |
732 | uint8_t p_response[16]; |
733 | uint8_t i_slot = p_sessions[i_session_id - 1].i_slot; |
734 | |
735 | if ( p_sessions[i_session_id - 1].pf_close != NULL((void*)0) ) |
736 | p_sessions[i_session_id - 1].pf_close( p_access, i_session_id ); |
737 | p_sessions[i_session_id - 1].i_resource_id = 0; |
738 | |
739 | p_response[0] = ST_CLOSE_SESSION_RESPONSE0x96; |
740 | p_response[1] = 0x3; |
741 | p_response[2] = SS_OK0x00; |
742 | p_response[3] = i_session_id >> 8; |
743 | p_response[4] = i_session_id & 0xff; |
744 | |
745 | if ( TPDUSend( p_access, i_slot, T_DATA_LAST0xA0, p_response, 5 ) != |
746 | 0 ) |
747 | { |
748 | msg_Err( p_access, |
749 | "SessionClose: couldn't send TPDU on slot %d", i_slot ); |
750 | return; |
751 | } |
752 | } |
753 | |
754 | /***************************************************************************** |
755 | * SPDUHandle |
756 | *****************************************************************************/ |
757 | static void SPDUHandle( access_t * p_access, uint8_t i_slot, |
758 | uint8_t *p_spdu, int i_size ) |
759 | { |
760 | int i_session_id; |
761 | |
762 | switch ( p_spdu[0] ) |
763 | { |
764 | case ST_SESSION_NUMBER0x90: |
765 | if ( i_size <= 4 ) |
766 | return; |
767 | i_session_id = (p_spdu[2] << 8) | p_spdu[3]; |
768 | if ( i_session_id <= MAX_SESSIONS32 |
769 | && p_sessions[i_session_id - 1].pf_handle != NULL((void*)0) ) |
770 | p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id, |
771 | p_spdu + 4, i_size - 4 ); |
772 | break; |
773 | |
774 | case ST_OPEN_SESSION_REQUEST0x91: |
775 | if ( i_size != 6 || p_spdu[1] != 0x4 ) |
776 | return; |
777 | SessionOpen( p_access, i_slot, p_spdu, i_size ); |
778 | break; |
779 | |
780 | case ST_CREATE_SESSION_RESPONSE0x94: |
781 | if ( i_size != 9 || p_spdu[1] != 0x7 ) |
782 | return; |
783 | SessionCreateResponse( p_access, i_slot, p_spdu, i_size ); |
784 | break; |
785 | |
786 | case ST_CLOSE_SESSION_REQUEST0x95: |
787 | if ( i_size != 4 || p_spdu[1] != 0x2 ) |
788 | return; |
789 | i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3]; |
790 | SessionClose( p_access, i_session_id ); |
791 | break; |
792 | |
793 | case ST_CLOSE_SESSION_RESPONSE0x96: |
794 | if ( i_size != 5 || p_spdu[1] != 0x3 ) |
795 | return; |
796 | i_session_id = ((int)p_spdu[3] << 8) | p_spdu[4]; |
797 | if ( p_spdu[2] ) |
798 | { |
799 | msg_Err( p_access, "closing a session which is not allocated (%d)", |
800 | i_session_id ); |
801 | } |
802 | else |
803 | { |
804 | if ( p_sessions[i_session_id - 1].pf_close != NULL((void*)0) ) |
805 | p_sessions[i_session_id - 1].pf_close( p_access, |
806 | i_session_id ); |
807 | p_sessions[i_session_id - 1].i_resource_id = 0; |
808 | } |
809 | break; |
810 | |
811 | default: |
812 | msg_Err( p_access, "unexpected tag in SPDUHandle (%x)", p_spdu[0] ); |
813 | break; |
814 | } |
815 | } |
816 | |
817 | |
818 | /* |
819 | * Application layer |
820 | */ |
821 | |
822 | #define AOT_NONE0x000000 0x000000 |
823 | #define AOT_PROFILE_ENQ0x9F8010 0x9F8010 |
824 | #define AOT_PROFILE0x9F8011 0x9F8011 |
825 | #define AOT_PROFILE_CHANGE0x9F8012 0x9F8012 |
826 | #define AOT_APPLICATION_INFO_ENQ0x9F8020 0x9F8020 |
827 | #define AOT_APPLICATION_INFO0x9F8021 0x9F8021 |
828 | #define AOT_ENTER_MENU0x9F8022 0x9F8022 |
829 | #define AOT_CA_INFO_ENQ0x9F8030 0x9F8030 |
830 | #define AOT_CA_INFO0x9F8031 0x9F8031 |
831 | #define AOT_CA_PMT0x9F8032 0x9F8032 |
832 | #define AOT_CA_PMT_REPLY0x9F8033 0x9F8033 |
833 | #define AOT_CA_UPDATE0x9F8034 0x9F8034 |
834 | #define AOT_TUNE0x9F8400 0x9F8400 |
835 | #define AOT_REPLACE0x9F8401 0x9F8401 |
836 | #define AOT_CLEAR_REPLACE0x9F8402 0x9F8402 |
837 | #define AOT_ASK_RELEASE0x9F8403 0x9F8403 |
838 | #define AOT_DATE_TIME_ENQ0x9F8440 0x9F8440 |
839 | #define AOT_DATE_TIME0x9F8441 0x9F8441 |
840 | #define AOT_CLOSE_MMI0x9F8800 0x9F8800 |
841 | #define AOT_DISPLAY_CONTROL0x9F8801 0x9F8801 |
842 | #define AOT_DISPLAY_REPLY0x9F8802 0x9F8802 |
843 | #define AOT_TEXT_LAST0x9F8803 0x9F8803 |
844 | #define AOT_TEXT_MORE0x9F8804 0x9F8804 |
845 | #define AOT_KEYPAD_CONTROL0x9F8805 0x9F8805 |
846 | #define AOT_KEYPRESS0x9F8806 0x9F8806 |
847 | #define AOT_ENQ0x9F8807 0x9F8807 |
848 | #define AOT_ANSW0x9F8808 0x9F8808 |
849 | #define AOT_MENU_LAST0x9F8809 0x9F8809 |
850 | #define AOT_MENU_MORE0x9F880A 0x9F880A |
851 | #define AOT_MENU_ANSW0x9F880B 0x9F880B |
852 | #define AOT_LIST_LAST0x9F880C 0x9F880C |
853 | #define AOT_LIST_MORE0x9F880D 0x9F880D |
854 | #define AOT_SUBTITLE_SEGMENT_LAST0x9F880E 0x9F880E |
855 | #define AOT_SUBTITLE_SEGMENT_MORE0x9F880F 0x9F880F |
856 | #define AOT_DISPLAY_MESSAGE0x9F8810 0x9F8810 |
857 | #define AOT_SCENE_END_MARK0x9F8811 0x9F8811 |
858 | #define AOT_SCENE_DONE0x9F8812 0x9F8812 |
859 | #define AOT_SCENE_CONTROL0x9F8813 0x9F8813 |
860 | #define AOT_SUBTITLE_DOWNLOAD_LAST0x9F8814 0x9F8814 |
861 | #define AOT_SUBTITLE_DOWNLOAD_MORE0x9F8815 0x9F8815 |
862 | #define AOT_FLUSH_DOWNLOAD0x9F8816 0x9F8816 |
863 | #define AOT_DOWNLOAD_REPLY0x9F8817 0x9F8817 |
864 | #define AOT_COMMS_CMD0x9F8C00 0x9F8C00 |
865 | #define AOT_CONNECTION_DESCRIPTOR0x9F8C01 0x9F8C01 |
866 | #define AOT_COMMS_REPLY0x9F8C02 0x9F8C02 |
867 | #define AOT_COMMS_SEND_LAST0x9F8C03 0x9F8C03 |
868 | #define AOT_COMMS_SEND_MORE0x9F8C04 0x9F8C04 |
869 | #define AOT_COMMS_RCV_LAST0x9F8C05 0x9F8C05 |
870 | #define AOT_COMMS_RCV_MORE0x9F8C06 0x9F8C06 |
871 | |
872 | /***************************************************************************** |
873 | * APDUGetTag |
874 | *****************************************************************************/ |
875 | static int APDUGetTag( const uint8_t *p_apdu, int i_size ) |
876 | { |
877 | if ( i_size >= 3 ) |
878 | { |
879 | int i, t = 0; |
880 | for ( i = 0; i < 3; i++ ) |
881 | t = (t << 8) | *p_apdu++; |
882 | return t; |
883 | } |
884 | |
885 | return AOT_NONE0x000000; |
886 | } |
887 | |
888 | /***************************************************************************** |
889 | * APDUGetLength |
890 | *****************************************************************************/ |
891 | static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size ) |
892 | { |
893 | return GetLength( &p_apdu[3], pi_size ); |
894 | } |
895 | |
896 | /***************************************************************************** |
897 | * APDUSend |
898 | *****************************************************************************/ |
899 | static int APDUSend( access_t * p_access, int i_session_id, int i_tag, |
900 | uint8_t *p_data, int i_size ) |
901 | { |
902 | uint8_t *p_apdu = malloc( i_size + 12 ); |
903 | uint8_t *p = p_apdu; |
904 | ca_msg_t ca_msg; |
905 | int i_ret; |
906 | |
907 | *p++ = (i_tag >> 16); |
908 | *p++ = (i_tag >> 8) & 0xff; |
909 | *p++ = i_tag & 0xff; |
910 | p = SetLength( p, i_size ); |
911 | if ( i_size ) |
912 | memcpy( p, p_data, i_size ); |
913 | if ( i_ca_type == CA_CI_LINK2 ) |
914 | { |
915 | i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu ); |
916 | } |
917 | else |
918 | { |
919 | if ( i_size + p - p_apdu > 256 ) |
920 | { |
921 | msg_Err( p_access, "CAM: apdu overflow" ); |
922 | i_ret = -1; |
923 | } |
924 | else |
925 | { |
926 | ca_msg.length = i_size + p - p_apdu; |
927 | if ( i_size == 0 ) ca_msg.length=3; |
928 | memcpy( ca_msg.msg, p_apdu, i_size + p - p_apdu ); |
929 | i_ret = ioctl(i_ca_handle, CA_SEND_MSG(((1U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((133)) << 0) | ((((sizeof(ca_msg_t)))) << ((0 + 8)+8))), &ca_msg ); |
930 | if ( i_ret < 0 ) |
931 | { |
932 | msg_Err( p_access, "Error sending to CAM: %m" ); |
933 | i_ret = -1; |
934 | } |
935 | } |
936 | } |
937 | free( p_apdu ); |
938 | return i_ret; |
939 | } |
940 | |
941 | /* |
942 | * Resource Manager |
943 | */ |
944 | |
945 | /***************************************************************************** |
946 | * ResourceManagerHandle |
947 | *****************************************************************************/ |
948 | static void ResourceManagerHandle( access_t * p_access, int i_session_id, |
949 | uint8_t *p_apdu, int i_size ) |
950 | { |
951 | int i_tag = APDUGetTag( p_apdu, i_size ); |
952 | |
953 | switch ( i_tag ) |
954 | { |
955 | case AOT_PROFILE_ENQ0x9F8010: |
956 | { |
957 | int resources[] = { htonl(RI_RESOURCE_MANAGER)(__extension__ ({ unsigned int __v, __x = (0x00010041); if (__builtin_constant_p (__x)) __v = ((((__x) & 0xff000000) >> 24) | (((__x ) & 0x00ff0000) >> 8) | (((__x) & 0x0000ff00) << 8) | (((__x) & 0x000000ff) << 24)); else __asm__ ( "bswap %0" : "=r" (__v) : "0" (__x)); __v; })), |
958 | htonl(RI_APPLICATION_INFORMATION)(__extension__ ({ unsigned int __v, __x = (0x00020041); if (__builtin_constant_p (__x)) __v = ((((__x) & 0xff000000) >> 24) | (((__x ) & 0x00ff0000) >> 8) | (((__x) & 0x0000ff00) << 8) | (((__x) & 0x000000ff) << 24)); else __asm__ ( "bswap %0" : "=r" (__v) : "0" (__x)); __v; })), |
959 | htonl(RI_CONDITIONAL_ACCESS_SUPPORT)(__extension__ ({ unsigned int __v, __x = (0x00030041); if (__builtin_constant_p (__x)) __v = ((((__x) & 0xff000000) >> 24) | (((__x ) & 0x00ff0000) >> 8) | (((__x) & 0x0000ff00) << 8) | (((__x) & 0x000000ff) << 24)); else __asm__ ( "bswap %0" : "=r" (__v) : "0" (__x)); __v; })), |
960 | htonl(RI_DATE_TIME)(__extension__ ({ unsigned int __v, __x = (0x00240041); if (__builtin_constant_p (__x)) __v = ((((__x) & 0xff000000) >> 24) | (((__x ) & 0x00ff0000) >> 8) | (((__x) & 0x0000ff00) << 8) | (((__x) & 0x000000ff) << 24)); else __asm__ ( "bswap %0" : "=r" (__v) : "0" (__x)); __v; })), |
961 | htonl(RI_MMI)(__extension__ ({ unsigned int __v, __x = (0x00400041); if (__builtin_constant_p (__x)) __v = ((((__x) & 0xff000000) >> 24) | (((__x ) & 0x00ff0000) >> 8) | (((__x) & 0x0000ff00) << 8) | (((__x) & 0x000000ff) << 24)); else __asm__ ( "bswap %0" : "=r" (__v) : "0" (__x)); __v; })) |
962 | }; |
963 | APDUSend( p_access, i_session_id, AOT_PROFILE0x9F8011, (uint8_t*)resources, |
964 | sizeof(resources) ); |
965 | break; |
966 | } |
967 | case AOT_PROFILE0x9F8011: |
968 | APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE0x9F8012, NULL((void*)0), 0 ); |
969 | break; |
970 | |
971 | default: |
972 | msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)", |
973 | i_tag ); |
974 | } |
975 | } |
976 | |
977 | /***************************************************************************** |
978 | * ResourceManagerOpen |
979 | *****************************************************************************/ |
980 | static void ResourceManagerOpen( access_t * p_access, int i_session_id ) |
981 | { |
982 | |
983 | msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id ); |
984 | |
985 | p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle; |
986 | |
987 | APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ0x9F8010, NULL((void*)0), 0 ); |
988 | } |
989 | |
990 | /* |
991 | * Application Information |
992 | */ |
993 | |
994 | /***************************************************************************** |
995 | * ApplicationInformationEnterMenu |
996 | *****************************************************************************/ |
997 | static void ApplicationInformationEnterMenu( access_t * p_access, |
998 | int i_session_id ) |
999 | { |
1000 | int i_slot = p_sessions[i_session_id - 1].i_slot; |
1001 | |
1002 | msg_Dbg( p_access, "entering MMI menus on session %d", i_session_id ); |
1003 | APDUSend( p_access, i_session_id, AOT_ENTER_MENU0x9F8022, NULL((void*)0), 0 ); |
1004 | p_slots[i_slot].b_mmi_expected = true1; |
1005 | } |
1006 | |
1007 | /***************************************************************************** |
1008 | * ApplicationInformationHandle |
1009 | *****************************************************************************/ |
1010 | static void ApplicationInformationHandle( access_t * p_access, int i_session_id, |
1011 | uint8_t *p_apdu, int i_size ) |
1012 | { |
1013 | int i_tag = APDUGetTag( p_apdu, i_size ); |
1014 | |
1015 | switch ( i_tag ) |
1016 | { |
1017 | case AOT_APPLICATION_INFO0x9F8021: |
1018 | { |
1019 | int i_type, i_manufacturer, i_code; |
1020 | int l = 0; |
1021 | uint8_t *d = APDUGetLength( p_apdu, &l ); |
1022 | |
1023 | if ( l < 4 ) break; |
1024 | |
1025 | i_type = *d++; |
1026 | i_manufacturer = ((int)d[0] << 8) | d[1]; |
1027 | d += 2; |
1028 | i_code = ((int)d[0] << 8) | d[1]; |
1029 | d += 2; |
1030 | d = GetLength( d, &l ); |
1031 | |
1032 | { |
1033 | char *psz_name = malloc(l + 1); |
1034 | memcpy( psz_name, d, l ); |
1035 | psz_name[l] = '\0'; |
1036 | msg_Info( p_access, "CAM: %s, %02X, %04X, %04X", |
1037 | psz_name, i_type, i_manufacturer, i_code ); |
1038 | switch (i_print_type) |
1039 | { |
1040 | case PRINT_XML: |
1041 | psz_name = dvb_string_xml_escape(psz_name); |
1042 | printf("<STATUS type=\"cam\" status=\"1\" cam_name=\"%s\" cam_type=\"%d\" cam_manufacturer=\"%d\" cam_product=\"%d\" />\n", |
1043 | psz_name, i_type, i_manufacturer, i_code); |
1044 | break; |
1045 | default: |
1046 | printf("CAM: name=%s type=%d manufacturer=%d product=%d\n", |
1047 | psz_name, i_type, i_manufacturer, i_code); |
1048 | } |
1049 | free(psz_name); |
1050 | } |
1051 | break; |
1052 | } |
1053 | default: |
1054 | msg_Err( p_access, |
1055 | "unexpected tag in ApplicationInformationHandle (0x%x)", |
1056 | i_tag ); |
1057 | } |
1058 | } |
1059 | |
1060 | /***************************************************************************** |
1061 | * ApplicationInformationOpen |
1062 | *****************************************************************************/ |
1063 | static void ApplicationInformationOpen( access_t * p_access, int i_session_id ) |
1064 | { |
1065 | |
1066 | msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id ); |
1067 | |
1068 | p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle; |
1069 | |
1070 | APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ0x9F8020, NULL((void*)0), 0 ); |
1071 | } |
1072 | |
1073 | /* |
1074 | * Conditional Access |
1075 | */ |
1076 | |
1077 | typedef struct |
1078 | { |
1079 | int i_nb_system_ids; |
1080 | uint16_t *pi_system_ids; |
1081 | |
1082 | int i_selected_programs; |
1083 | int b_high_level; |
1084 | } system_ids_t; |
1085 | |
1086 | static bool_Bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id ) |
1087 | { |
1088 | int i; |
1089 | if( p_ids == NULL((void*)0) ) return false0; |
1090 | if( p_ids->b_high_level ) return true1; |
1091 | |
1092 | for ( i = 0; i < p_ids->i_nb_system_ids; i++ ) |
1093 | if ( p_ids->pi_system_ids[i] == i_id ) |
1094 | return true1; |
1095 | |
1096 | return false0; |
1097 | } |
1098 | |
1099 | /***************************************************************************** |
1100 | * CAPMTBuild |
1101 | *****************************************************************************/ |
1102 | static bool_Bool HasCADescriptors( system_ids_t *p_ids, uint8_t *p_descs ) |
1103 | { |
1104 | const uint8_t *p_desc; |
1105 | uint16_t j = 0; |
1106 | |
1107 | while ( (p_desc = descs_get_desc( p_descs, j )) != NULL((void*)0) ) |
1108 | { |
1109 | uint8_t i_tag = desc_get_tag( p_desc ); |
1110 | j++; |
1111 | |
1112 | if ( i_tag == 0x9 && desc09_validate( p_desc ) |
1113 | && CheckSystemID( p_ids, desc09_get_sysid( p_desc ) ) ) |
1114 | return true1; |
1115 | } |
1116 | |
1117 | return false0; |
1118 | } |
1119 | |
1120 | static void CopyCADescriptors( system_ids_t *p_ids, uint8_t i_cmd, |
1121 | uint8_t *p_infos, uint8_t *p_descs ) |
1122 | { |
1123 | const uint8_t *p_desc; |
1124 | uint16_t j = 0, k = 0; |
1125 | |
1126 | capmti_init( p_infos ); |
1127 | capmti_set_length( p_infos, 0xfff ); |
1128 | capmti_set_cmd( p_infos, i_cmd ); |
1129 | |
1130 | while ( (p_desc = descs_get_desc( p_descs, j )) != NULL((void*)0) ) |
1131 | { |
1132 | uint8_t i_tag = desc_get_tag( p_desc ); |
1133 | j++; |
1134 | |
1135 | if ( i_tag == 0x9 && desc09_validate( p_desc ) |
1136 | && CheckSystemID( p_ids, desc09_get_sysid( p_desc ) ) ) |
1137 | { |
1138 | uint8_t *p_info = capmti_get_info( p_infos, k ); |
1139 | k++; |
1140 | memcpy( p_info, p_desc, |
1141 | DESC_HEADER_SIZE2 + desc_get_length( p_desc ) ); |
1142 | } |
1143 | } |
1144 | |
1145 | if ( k ) |
1146 | { |
1147 | uint8_t *p_info = capmti_get_info( p_infos, k ); |
1148 | capmti_set_length( p_infos, p_info - p_infos - DESCS_HEADER_SIZE2 ); |
1149 | } |
1150 | else |
1151 | capmti_set_length( p_infos, 0 ); |
1152 | } |
1153 | |
1154 | static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id, |
1155 | uint8_t *p_pmt, uint8_t i_list_mgt, |
1156 | uint8_t i_cmd, int *pi_capmt_size ) |
1157 | { |
1158 | system_ids_t *p_ids = |
1159 | (system_ids_t *)p_sessions[i_session_id - 1].p_sys; |
1160 | uint8_t *p_es; |
1161 | uint8_t *p_capmt, *p_capmt_n; |
1162 | uint16_t j, k; |
1163 | bool_Bool b_has_ca = HasCADescriptors( p_ids, pmt_get_descs( p_pmt ) ); |
1164 | bool_Bool b_has_es = false0; |
1165 | |
1166 | j = 0; |
1167 | while ( (p_es = pmt_get_es( p_pmt, j )) != NULL((void*)0) ) |
1168 | { |
1169 | uint16_t i_pid = pmtn_get_pid( p_es ); |
1170 | j++; |
1171 | |
1172 | if ( demux_PIDIsSelected( i_pid ) ) |
1173 | { |
1174 | b_has_es = true1; |
1175 | b_has_ca = b_has_ca |
1176 | || HasCADescriptors( p_ids, pmtn_get_descs( p_es ) ); |
1177 | } |
1178 | } |
1179 | |
1180 | if ( !b_has_es ) |
1181 | { |
1182 | *pi_capmt_size = 0; |
1183 | return NULL((void*)0); |
1184 | } |
1185 | |
1186 | if ( !b_has_ca ) |
1187 | { |
1188 | msg_Warn( p_access, |
1189 | "no compatible scrambling system for SID %d on session %d", |
1190 | pmt_get_programpsi_get_tableidext( p_pmt ), i_session_id ); |
1191 | *pi_capmt_size = 0; |
1192 | return NULL((void*)0); |
1193 | } |
1194 | |
1195 | p_capmt = capmt_allocatepsi_private_allocate(); |
1196 | capmt_init( p_capmt ); |
1197 | capmt_set_listmanagement( p_capmt, i_list_mgt ); |
1198 | capmt_set_program( p_capmt, pmt_get_programpsi_get_tableidext( p_pmt ) ); |
1199 | capmt_set_version( p_capmt, psi_get_version( p_pmt ) ); |
1200 | |
1201 | CopyCADescriptors( p_ids, i_cmd, capmt_get_infos( p_capmt ), |
1202 | pmt_get_descs( p_pmt ) ); |
1203 | |
1204 | j = 0; k = 0; |
1205 | while ( (p_es = pmt_get_es( p_pmt, j )) != NULL((void*)0) ) |
1206 | { |
1207 | uint16_t i_pid = pmtn_get_pid( p_es ); |
1208 | j++; |
1209 | |
1210 | if ( !demux_PIDIsSelected( i_pid ) ) |
1211 | continue; |
1212 | |
1213 | p_capmt_n = capmt_get_es( p_capmt, k ); |
1214 | k++; |
1215 | |
1216 | capmtn_init( p_capmt_n ); |
1217 | capmtn_set_streamtype( p_capmt_n, pmtn_get_streamtype( p_es ) ); |
1218 | capmtn_set_pid( p_capmt_n, pmtn_get_pid( p_es ) ); |
1219 | |
1220 | CopyCADescriptors( p_ids, i_cmd, capmtn_get_infos( p_capmt_n ), |
1221 | pmtn_get_descs( p_es ) ); |
1222 | } |
1223 | |
1224 | p_capmt_n = capmt_get_es( p_capmt, k ); |
1225 | *pi_capmt_size = p_capmt_n - p_capmt; |
1226 | |
1227 | return p_capmt; |
1228 | } |
1229 | |
1230 | /***************************************************************************** |
1231 | * CAPMTFirst |
1232 | *****************************************************************************/ |
1233 | static void CAPMTFirst( access_t * p_access, int i_session_id, uint8_t *p_pmt ) |
1234 | { |
1235 | uint8_t *p_capmt; |
1236 | int i_capmt_size; |
1237 | |
1238 | msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d", |
1239 | pmt_get_programpsi_get_tableidext( p_pmt ), i_session_id ); |
1240 | |
1241 | p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt, |
1242 | 0x3 /* only */, 0x1 /* ok_descrambling */, |
1243 | &i_capmt_size ); |
1244 | |
1245 | if ( i_capmt_size ) |
1246 | { |
1247 | APDUSend( p_access, i_session_id, AOT_CA_PMT0x9F8032, p_capmt, i_capmt_size ); |
1248 | free( p_capmt ); |
1249 | } |
1250 | } |
1251 | |
1252 | /***************************************************************************** |
1253 | * CAPMTAdd |
1254 | *****************************************************************************/ |
1255 | static void CAPMTAdd( access_t * p_access, int i_session_id, uint8_t *p_pmt ) |
1256 | { |
1257 | system_ids_t *p_ids = |
1258 | (system_ids_t *)p_sessions[i_session_id - 1].p_sys; |
1259 | uint8_t *p_capmt; |
1260 | int i_capmt_size; |
1261 | |
1262 | p_ids->i_selected_programs++; |
1263 | if( p_ids->i_selected_programs == 1 ) |
1264 | { |
1265 | CAPMTFirst( p_access, i_session_id, p_pmt ); |
1266 | return; |
1267 | } |
1268 | |
1269 | msg_Dbg( p_access, "adding CAPMT for SID %d on session %d", |
1270 | pmt_get_programpsi_get_tableidext( p_pmt ), i_session_id ); |
1271 | |
1272 | p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt, |
1273 | 0x4 /* add */, 0x1 /* ok_descrambling */, |
1274 | &i_capmt_size ); |
1275 | |
1276 | if ( i_capmt_size ) |
1277 | { |
1278 | APDUSend( p_access, i_session_id, AOT_CA_PMT0x9F8032, p_capmt, i_capmt_size ); |
1279 | free( p_capmt ); |
1280 | } |
1281 | } |
1282 | |
1283 | /***************************************************************************** |
1284 | * CAPMTUpdate |
1285 | *****************************************************************************/ |
1286 | static void CAPMTUpdate( access_t * p_access, int i_session_id, uint8_t *p_pmt ) |
1287 | { |
1288 | uint8_t *p_capmt; |
1289 | int i_capmt_size; |
1290 | |
1291 | msg_Dbg( p_access, "updating CAPMT for SID %d on session %d", |
1292 | pmt_get_programpsi_get_tableidext( p_pmt ), i_session_id ); |
1293 | |
1294 | p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt, |
1295 | 0x5 /* update */, 0x1 /* ok_descrambling */, |
1296 | &i_capmt_size ); |
1297 | |
1298 | if ( i_capmt_size ) |
1299 | { |
1300 | APDUSend( p_access, i_session_id, AOT_CA_PMT0x9F8032, p_capmt, i_capmt_size ); |
1301 | free( p_capmt ); |
1302 | } |
1303 | } |
1304 | |
1305 | /***************************************************************************** |
1306 | * CAPMTDelete |
1307 | *****************************************************************************/ |
1308 | static void CAPMTDelete( access_t * p_access, int i_session_id, uint8_t *p_pmt ) |
1309 | { |
1310 | system_ids_t *p_ids = |
1311 | (system_ids_t *)p_sessions[i_session_id - 1].p_sys; |
1312 | uint8_t *p_capmt; |
1313 | int i_capmt_size; |
1314 | |
1315 | p_ids->i_selected_programs--; |
1316 | msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d", |
1317 | pmt_get_programpsi_get_tableidext( p_pmt ), i_session_id ); |
1318 | |
1319 | p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt, |
1320 | 0x5 /* update */, 0x4 /* not selected */, |
1321 | &i_capmt_size ); |
1322 | |
1323 | if ( i_capmt_size ) |
1324 | { |
1325 | APDUSend( p_access, i_session_id, AOT_CA_PMT0x9F8032, p_capmt, i_capmt_size ); |
1326 | free( p_capmt ); |
1327 | } |
1328 | } |
1329 | |
1330 | /***************************************************************************** |
1331 | * ConditionalAccessHandle |
1332 | *****************************************************************************/ |
1333 | static void ConditionalAccessHandle( access_t * p_access, int i_session_id, |
1334 | uint8_t *p_apdu, int i_size ) |
1335 | { |
1336 | system_ids_t *p_ids = |
1337 | (system_ids_t *)p_sessions[i_session_id - 1].p_sys; |
1338 | int i_tag = APDUGetTag( p_apdu, i_size ); |
1339 | |
1340 | switch ( i_tag ) |
1341 | { |
1342 | case AOT_CA_INFO0x9F8031: |
1343 | { |
1344 | int i; |
1345 | int l = 0; |
1346 | uint8_t *d = APDUGetLength( p_apdu, &l ); |
1347 | msg_Dbg( p_access, "CA system IDs supported by the application :" ); |
1348 | |
1349 | if ( p_ids->i_nb_system_ids ) |
1350 | free( p_ids->pi_system_ids ); |
1351 | p_ids->i_nb_system_ids = l / 2; |
1352 | p_ids->pi_system_ids = malloc( p_ids->i_nb_system_ids |
1353 | * sizeof(uint16_t) ); |
1354 | |
1355 | for ( i = 0; i < p_ids->i_nb_system_ids; i++ ) |
1356 | { |
1357 | p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1]; |
1358 | d += 2; |
1359 | msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] ); |
1360 | } |
1361 | |
1362 | demux_ResendCAPMTs(); |
1363 | break; |
1364 | } |
1365 | |
1366 | case AOT_CA_UPDATE0x9F8034: |
1367 | /* http://www.cablelabs.com/specifications/OC-SP-HOSTPOD-IF-I08-011221.pdf */ |
1368 | case AOT_CA_PMT_REPLY0x9F8033: |
1369 | /* We do not care */ |
1370 | break; |
1371 | |
1372 | default: |
1373 | msg_Err( p_access, |
1374 | "unexpected tag in ConditionalAccessHandle (0x%x)", |
1375 | i_tag ); |
1376 | } |
1377 | } |
1378 | |
1379 | /***************************************************************************** |
1380 | * ConditionalAccessClose |
1381 | *****************************************************************************/ |
1382 | static void ConditionalAccessClose( access_t * p_access, int i_session_id ) |
1383 | { |
1384 | |
1385 | msg_Dbg( p_access, "closing ConditionalAccess session (%d)", i_session_id ); |
1386 | |
1387 | free( p_sessions[i_session_id - 1].p_sys ); |
1388 | } |
1389 | |
1390 | /***************************************************************************** |
1391 | * ConditionalAccessOpen |
1392 | *****************************************************************************/ |
1393 | static void ConditionalAccessOpen( access_t * p_access, int i_session_id ) |
1394 | { |
1395 | |
1396 | msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id ); |
1397 | |
1398 | p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle; |
1399 | p_sessions[i_session_id - 1].pf_close = ConditionalAccessClose; |
1400 | p_sessions[i_session_id - 1].p_sys = malloc(sizeof(system_ids_t)); |
1401 | memset( p_sessions[i_session_id - 1].p_sys, 0, |
1402 | sizeof(system_ids_t) ); |
1403 | |
1404 | APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ0x9F8030, NULL((void*)0), 0 ); |
1405 | } |
1406 | |
1407 | /* |
1408 | * Date Time |
1409 | */ |
1410 | |
1411 | typedef struct |
1412 | { |
1413 | int i_interval; |
1414 | mtime_t i_last; |
1415 | } date_time_t; |
1416 | |
1417 | /***************************************************************************** |
1418 | * DateTimeSend |
1419 | *****************************************************************************/ |
1420 | static void DateTimeSend( access_t * p_access, int i_session_id ) |
1421 | { |
1422 | date_time_t *p_date = |
1423 | (date_time_t *)p_sessions[i_session_id - 1].p_sys; |
1424 | |
1425 | time_t t = time(NULL((void*)0)); |
1426 | struct tm tm_gmt; |
1427 | struct tm tm_loc; |
1428 | |
1429 | if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) ) |
1430 | { |
1431 | int Y = tm_gmt.tm_year; |
1432 | int M = tm_gmt.tm_mon + 1; |
1433 | int D = tm_gmt.tm_mday; |
1434 | int L = (M == 1 || M == 2) ? 1 : 0; |
1435 | int MJD = 14956 + D + (int)((Y - L) * 365.25) |
1436 | + (int)((M + 1 + L * 12) * 30.6001); |
1437 | uint8_t p_response[7]; |
1438 | |
1439 | #define DEC2BCD(d)(((d / 10) << 4) + (d % 10)) (((d / 10) << 4) + (d % 10)) |
1440 | |
1441 | p_response[0] = htons(MJD)(__extension__ ({ unsigned short int __v, __x = (unsigned short int) (MJD); if (__builtin_constant_p (__x)) __v = ((unsigned short int) ((((__x) >> 8) & 0xff) | (((__x) & 0xff ) << 8))); else __asm__ ("rorw $8, %w0" : "=r" (__v) : "0" (__x) : "cc"); __v; })) >> 8; |
1442 | p_response[1] = htons(MJD)(__extension__ ({ unsigned short int __v, __x = (unsigned short int) (MJD); if (__builtin_constant_p (__x)) __v = ((unsigned short int) ((((__x) >> 8) & 0xff) | (((__x) & 0xff ) << 8))); else __asm__ ("rorw $8, %w0" : "=r" (__v) : "0" (__x) : "cc"); __v; })) & 0xff; |
1443 | p_response[2] = DEC2BCD(tm_gmt.tm_hour)(((tm_gmt.tm_hour / 10) << 4) + (tm_gmt.tm_hour % 10)); |
1444 | p_response[3] = DEC2BCD(tm_gmt.tm_min)(((tm_gmt.tm_min / 10) << 4) + (tm_gmt.tm_min % 10)); |
1445 | p_response[4] = DEC2BCD(tm_gmt.tm_sec)(((tm_gmt.tm_sec / 10) << 4) + (tm_gmt.tm_sec % 10)); |
1446 | p_response[5] = htons(tm_loc.tm_gmtoff / 60)(__extension__ ({ unsigned short int __v, __x = (unsigned short int) (tm_loc.tm_gmtoff / 60); if (__builtin_constant_p (__x) ) __v = ((unsigned short int) ((((__x) >> 8) & 0xff ) | (((__x) & 0xff) << 8))); else __asm__ ("rorw $8, %w0" : "=r" (__v) : "0" (__x) : "cc"); __v; })) >> 8; |
1447 | p_response[6] = htons(tm_loc.tm_gmtoff / 60)(__extension__ ({ unsigned short int __v, __x = (unsigned short int) (tm_loc.tm_gmtoff / 60); if (__builtin_constant_p (__x) ) __v = ((unsigned short int) ((((__x) >> 8) & 0xff ) | (((__x) & 0xff) << 8))); else __asm__ ("rorw $8, %w0" : "=r" (__v) : "0" (__x) : "cc"); __v; })) & 0xff; |
1448 | |
1449 | APDUSend( p_access, i_session_id, AOT_DATE_TIME0x9F8441, p_response, 7 ); |
1450 | |
1451 | p_date->i_last = i_wallclock; |
1452 | } |
1453 | } |
1454 | |
1455 | /***************************************************************************** |
1456 | * DateTimeHandle |
1457 | *****************************************************************************/ |
1458 | static void DateTimeHandle( access_t * p_access, int i_session_id, |
1459 | uint8_t *p_apdu, int i_size ) |
1460 | { |
1461 | date_time_t *p_date = |
1462 | (date_time_t *)p_sessions[i_session_id - 1].p_sys; |
1463 | |
1464 | int i_tag = APDUGetTag( p_apdu, i_size ); |
1465 | |
1466 | switch ( i_tag ) |
1467 | { |
1468 | case AOT_DATE_TIME_ENQ0x9F8440: |
1469 | { |
1470 | int l; |
1471 | const uint8_t *d = APDUGetLength( p_apdu, &l ); |
1472 | |
1473 | if ( l > 0 ) |
1474 | { |
1475 | p_date->i_interval = *d; |
1476 | msg_Dbg( p_access, "DateTimeHandle : interval set to %d", |
1477 | p_date->i_interval ); |
1478 | } |
1479 | else |
1480 | p_date->i_interval = 0; |
1481 | |
1482 | DateTimeSend( p_access, i_session_id ); |
1483 | break; |
1484 | } |
1485 | default: |
1486 | msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag ); |
1487 | } |
1488 | } |
1489 | |
1490 | /***************************************************************************** |
1491 | * DateTimeManage |
1492 | *****************************************************************************/ |
1493 | static void DateTimeManage( access_t * p_access, int i_session_id ) |
1494 | { |
1495 | date_time_t *p_date = |
1496 | (date_time_t *)p_sessions[i_session_id - 1].p_sys; |
1497 | |
1498 | if ( p_date->i_interval |
1499 | && i_wallclock > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 ) |
1500 | { |
1501 | DateTimeSend( p_access, i_session_id ); |
1502 | } |
1503 | } |
1504 | |
1505 | /***************************************************************************** |
1506 | * DateTimeClose |
1507 | *****************************************************************************/ |
1508 | static void DateTimeClose( access_t * p_access, int i_session_id ) |
1509 | { |
1510 | msg_Dbg( p_access, "closing DateTime session (%d)", i_session_id ); |
1511 | |
1512 | free( p_sessions[i_session_id - 1].p_sys ); |
1513 | } |
1514 | |
1515 | /***************************************************************************** |
1516 | * DateTimeOpen |
1517 | *****************************************************************************/ |
1518 | static void DateTimeOpen( access_t * p_access, int i_session_id ) |
1519 | { |
1520 | msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id ); |
1521 | |
1522 | p_sessions[i_session_id - 1].pf_handle = DateTimeHandle; |
1523 | p_sessions[i_session_id - 1].pf_manage = DateTimeManage; |
1524 | p_sessions[i_session_id - 1].pf_close = DateTimeClose; |
1525 | p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t)); |
1526 | memset( p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) ); |
1527 | |
1528 | DateTimeSend( p_access, i_session_id ); |
1529 | } |
1530 | |
1531 | /* |
1532 | * MMI |
1533 | */ |
1534 | |
1535 | /* Display Control Commands */ |
1536 | |
1537 | #define DCC_SET_MMI_MODE0x01 0x01 |
1538 | #define DCC_DISPLAY_CHARACTER_TABLE_LIST0x02 0x02 |
1539 | #define DCC_INPUT_CHARACTER_TABLE_LIST0x03 0x03 |
1540 | #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS0x04 0x04 |
1541 | #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS0x05 0x05 |
1542 | |
1543 | /* MMI Modes */ |
1544 | |
1545 | #define MM_HIGH_LEVEL0x01 0x01 |
1546 | #define MM_LOW_LEVEL_OVERLAY_GRAPHICS0x02 0x02 |
1547 | #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS0x03 0x03 |
1548 | |
1549 | /* Display Reply IDs */ |
1550 | |
1551 | #define DRI_MMI_MODE_ACK0x01 0x01 |
1552 | #define DRI_LIST_DISPLAY_CHARACTER_TABLES0x02 0x02 |
1553 | #define DRI_LIST_INPUT_CHARACTER_TABLES0x03 0x03 |
1554 | #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS0x04 0x04 |
1555 | #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS0x05 0x05 |
1556 | #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD0xF0 0xF0 |
1557 | #define DRI_UNKNOWN_MMI_MODE0xF1 0xF1 |
1558 | #define DRI_UNKNOWN_CHARACTER_TABLE0xF2 0xF2 |
1559 | |
1560 | /* Enquiry Flags */ |
1561 | |
1562 | #define EF_BLIND0x01 0x01 |
1563 | |
1564 | /* Answer IDs */ |
1565 | |
1566 | #define AI_CANCEL0x00 0x00 |
1567 | #define AI_ANSWER0x01 0x01 |
1568 | |
1569 | typedef struct |
1570 | { |
1571 | en50221_mmi_object_t last_object; |
1572 | } mmi_t; |
1573 | |
1574 | static inline void en50221_MMIFree( en50221_mmi_object_t *p_object ) |
1575 | { |
1576 | int i; |
1577 | |
1578 | switch ( p_object->i_object_type ) |
1579 | { |
1580 | case EN50221_MMI_ENQ1: |
1581 | free( p_object->u.enq.psz_text ); |
1582 | break; |
1583 | |
1584 | case EN50221_MMI_ANSW2: |
1585 | if ( p_object->u.answ.b_ok ) |
1586 | { |
1587 | free( p_object->u.answ.psz_answ ); |
1588 | } |
1589 | break; |
1590 | |
1591 | case EN50221_MMI_MENU3: |
1592 | case EN50221_MMI_LIST5: |
1593 | free( p_object->u.menu.psz_title ); |
1594 | free( p_object->u.menu.psz_subtitle ); |
1595 | free( p_object->u.menu.psz_bottom ); |
1596 | for ( i = 0; i < p_object->u.menu.i_choices; i++ ) |
1597 | { |
1598 | free( p_object->u.menu.ppsz_choices[i] ); |
1599 | } |
1600 | free( p_object->u.menu.ppsz_choices ); |
1601 | break; |
1602 | |
1603 | default: |
1604 | break; |
1605 | } |
1606 | } |
1607 | |
1608 | /***************************************************************************** |
1609 | * MMISendObject |
1610 | *****************************************************************************/ |
1611 | static void MMISendObject( access_t *p_access, int i_session_id, |
1612 | en50221_mmi_object_t *p_object ) |
1613 | { |
1614 | int i_slot = p_sessions[i_session_id - 1].i_slot; |
1615 | uint8_t *p_data; |
1616 | int i_size, i_tag; |
1617 | |
1618 | switch ( p_object->i_object_type ) |
1619 | { |
1620 | case EN50221_MMI_ANSW2: |
1621 | i_tag = AOT_ANSW0x9F8808; |
1622 | i_size = 1 + strlen( p_object->u.answ.psz_answ ); |
1623 | p_data = malloc( i_size ); |
1624 | p_data[0] = (p_object->u.answ.b_ok == true1) ? 0x1 : 0x0; |
1625 | strncpy( (char *)&p_data[1], p_object->u.answ.psz_answ, i_size - 1 )__builtin_strncpy ((char *)&p_data[1], p_object->u.answ .psz_answ, i_size - 1); |
1626 | break; |
1627 | |
1628 | case EN50221_MMI_MENU_ANSW4: |
1629 | i_tag = AOT_MENU_ANSW0x9F880B; |
1630 | i_size = 1; |
1631 | p_data = malloc( i_size ); |
1632 | p_data[0] = p_object->u.menu_answ.i_choice; |
1633 | break; |
1634 | |
1635 | default: |
1636 | msg_Err( p_access, "unknown MMI object %d", p_object->i_object_type ); |
1637 | return; |
1638 | } |
1639 | |
1640 | APDUSend( p_access, i_session_id, i_tag, p_data, i_size ); |
1641 | free( p_data ); |
1642 | |
1643 | p_slots[i_slot].b_mmi_expected = true1; |
1644 | } |
1645 | |
1646 | /***************************************************************************** |
1647 | * MMISendClose |
1648 | *****************************************************************************/ |
1649 | static void MMISendClose( access_t *p_access, int i_session_id ) |
1650 | { |
1651 | int i_slot = p_sessions[i_session_id - 1].i_slot; |
1652 | |
1653 | APDUSend( p_access, i_session_id, AOT_CLOSE_MMI0x9F8800, NULL((void*)0), 0 ); |
1654 | |
1655 | p_slots[i_slot].b_mmi_expected = true1; |
1656 | } |
1657 | |
1658 | /***************************************************************************** |
1659 | * MMIDisplayReply |
1660 | *****************************************************************************/ |
1661 | static void MMIDisplayReply( access_t *p_access, int i_session_id ) |
1662 | { |
1663 | uint8_t p_response[2]; |
1664 | |
1665 | p_response[0] = DRI_MMI_MODE_ACK0x01; |
1666 | p_response[1] = MM_HIGH_LEVEL0x01; |
1667 | |
1668 | APDUSend( p_access, i_session_id, AOT_DISPLAY_REPLY0x9F8802, p_response, 2 ); |
1669 | |
1670 | msg_Dbg( p_access, "sending DisplayReply on session (%d)", i_session_id ); |
1671 | } |
1672 | |
1673 | /***************************************************************************** |
1674 | * MMIGetText |
1675 | *****************************************************************************/ |
1676 | static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size ) |
1677 | { |
1678 | int i_tag = APDUGetTag( *pp_apdu, *pi_size ); |
1679 | int l; |
1680 | uint8_t *d; |
1681 | |
1682 | if ( i_tag != AOT_TEXT_LAST0x9F8803 ) |
1683 | { |
1684 | msg_Err( p_access, "unexpected text tag: %06x", i_tag ); |
1685 | *pi_size = 0; |
1686 | return strdup( "" )(__extension__ (__builtin_constant_p ("") && ((size_t )(const void *)(("") + 1) - (size_t)(const void *)("") == 1) ? (((const char *) (""))[0] == '\0' ? (char *) calloc ((size_t ) 1, (size_t) 1) : ({ size_t __len = strlen ("") + 1; char *__retval = (char *) malloc (__len); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval, "", __len); __retval; })) : __strdup (""))); |
1687 | } |
1688 | |
1689 | d = APDUGetLength( *pp_apdu, &l ); |
1690 | |
1691 | *pp_apdu += l + 4; |
1692 | *pi_size -= l + 4; |
1693 | |
1694 | return dvb_string_get( d, l, demux_Iconv, p_access ); |
1695 | } |
1696 | |
1697 | /***************************************************************************** |
1698 | * MMIHandleEnq |
1699 | *****************************************************************************/ |
1700 | static void MMIHandleEnq( access_t *p_access, int i_session_id, |
1701 | uint8_t *p_apdu, int i_size ) |
1702 | { |
1703 | mmi_t *p_mmi = (mmi_t *)p_sessions[i_session_id - 1].p_sys; |
1704 | int i_slot = p_sessions[i_session_id - 1].i_slot; |
1705 | int l; |
1706 | uint8_t *d = APDUGetLength( p_apdu, &l ); |
1707 | |
1708 | en50221_MMIFree( &p_mmi->last_object ); |
1709 | p_mmi->last_object.i_object_type = EN50221_MMI_ENQ1; |
1710 | p_mmi->last_object.u.enq.b_blind = (*d & 0x1) ? true1 : false0; |
1711 | d += 2; /* skip answer_text_length because it is not mandatory */ |
1712 | l -= 2; |
1713 | p_mmi->last_object.u.enq.psz_text = malloc( l + 1 ); |
1714 | strncpy( p_mmi->last_object.u.enq.psz_text, (char *)d, l )__builtin_strncpy (p_mmi->last_object.u.enq.psz_text, (char *)d, l); |
1715 | p_mmi->last_object.u.enq.psz_text[l] = '\0'; |
1716 | |
1717 | msg_Dbg( p_access, "MMI enq: %s%s", p_mmi->last_object.u.enq.psz_text, |
1718 | p_mmi->last_object.u.enq.b_blind == true1 ? " (blind)" : "" ); |
1719 | |
1720 | p_slots[i_slot].b_mmi_expected = false0; |
1721 | p_slots[i_slot].b_mmi_undisplayed = true1; |
1722 | } |
1723 | |
1724 | /***************************************************************************** |
1725 | * MMIHandleMenu |
1726 | *****************************************************************************/ |
1727 | static void MMIHandleMenu( access_t *p_access, int i_session_id, int i_tag, |
1728 | uint8_t *p_apdu, int i_size ) |
1729 | { |
1730 | mmi_t *p_mmi = (mmi_t *)p_sessions[i_session_id - 1].p_sys; |
1731 | int i_slot = p_sessions[i_session_id - 1].i_slot; |
1732 | int l; |
1733 | uint8_t *d = APDUGetLength( p_apdu, &l ); |
1734 | |
1735 | en50221_MMIFree( &p_mmi->last_object ); |
1736 | p_mmi->last_object.i_object_type = (i_tag == AOT_MENU_LAST0x9F8809) ? |
1737 | EN50221_MMI_MENU3 : EN50221_MMI_LIST5; |
1738 | p_mmi->last_object.u.menu.i_choices = 0; |
1739 | p_mmi->last_object.u.menu.ppsz_choices = NULL((void*)0); |
1740 | |
1741 | if ( l > 0 ) |
1742 | { |
1743 | l--; d++; /* choice_nb */ |
1744 | |
1745 | #define GET_FIELD( x ) \ |
1746 | if ( l > 0 ) \ |
1747 | { \ |
1748 | p_mmi->last_object.u.menu.psz_##x \ |
1749 | = MMIGetText( p_access, &d, &l ); \ |
1750 | msg_Dbg( p_access, "MMI " STRINGIFY( x )"x" ": %s", \ |
1751 | p_mmi->last_object.u.menu.psz_##x ); \ |
1752 | } |
1753 | |
1754 | GET_FIELD( title ); |
1755 | GET_FIELD( subtitle ); |
1756 | GET_FIELD( bottom ); |
1757 | #undef GET_FIELD |
1758 | |
1759 | while ( l > 0 ) |
1760 | { |
1761 | char *psz_text = MMIGetText( p_access, &d, &l ); |
1762 | TAB_APPEND( p_mmi->last_object.u.menu.i_choices,if( (p_mmi->last_object.u.menu.i_choices) > 0 ) { (p_mmi ->last_object.u.menu.ppsz_choices) = realloc( p_mmi->last_object .u.menu.ppsz_choices, sizeof( void ** ) * ( (p_mmi->last_object .u.menu.i_choices) + 1 ) ); } else { (p_mmi->last_object.u .menu.ppsz_choices) = malloc( sizeof( void ** ) ); } (p_mmi-> last_object.u.menu.ppsz_choices)[p_mmi->last_object.u.menu .i_choices] = (psz_text); (p_mmi->last_object.u.menu.i_choices )++ |
Result of 'malloc' is converted to a pointer of type 'char *', which is incompatible with sizeof operand type 'void **' | |
1763 | p_mmi->last_object.u.menu.ppsz_choices,if( (p_mmi->last_object.u.menu.i_choices) > 0 ) { (p_mmi ->last_object.u.menu.ppsz_choices) = realloc( p_mmi->last_object .u.menu.ppsz_choices, sizeof( void ** ) * ( (p_mmi->last_object .u.menu.i_choices) + 1 ) ); } else { (p_mmi->last_object.u .menu.ppsz_choices) = malloc( sizeof( void ** ) ); } (p_mmi-> last_object.u.menu.ppsz_choices)[p_mmi->last_object.u.menu .i_choices] = (psz_text); (p_mmi->last_object.u.menu.i_choices )++ |
1764 | psz_text )if( (p_mmi->last_object.u.menu.i_choices) > 0 ) { (p_mmi ->last_object.u.menu.ppsz_choices) = realloc( p_mmi->last_object .u.menu.ppsz_choices, sizeof( void ** ) * ( (p_mmi->last_object .u.menu.i_choices) + 1 ) ); } else { (p_mmi->last_object.u .menu.ppsz_choices) = malloc( sizeof( void ** ) ); } (p_mmi-> last_object.u.menu.ppsz_choices)[p_mmi->last_object.u.menu .i_choices] = (psz_text); (p_mmi->last_object.u.menu.i_choices )++; |
1765 | msg_Dbg( p_access, "MMI choice: %s", psz_text ); |
1766 | } |
1767 | } |
1768 | |
1769 | p_slots[i_slot].b_mmi_expected = false0; |
1770 | p_slots[i_slot].b_mmi_undisplayed = true1; |
1771 | } |
1772 | |
1773 | /***************************************************************************** |
1774 | * MMIHandle |
1775 | *****************************************************************************/ |
1776 | static void MMIHandle( access_t *p_access, int i_session_id, |
1777 | uint8_t *p_apdu, int i_size ) |
1778 | { |
1779 | int i_tag = APDUGetTag( p_apdu, i_size ); |
1780 | |
1781 | switch ( i_tag ) |
1782 | { |
1783 | case AOT_DISPLAY_CONTROL0x9F8801: |
1784 | { |
1785 | int l; |
1786 | uint8_t *d = APDUGetLength( p_apdu, &l ); |
1787 | |
1788 | if ( l > 0 ) |
1789 | { |
1790 | switch ( *d ) |
1791 | { |
1792 | case DCC_SET_MMI_MODE0x01: |
1793 | if ( l == 2 && d[1] == MM_HIGH_LEVEL0x01 ) |
1794 | MMIDisplayReply( p_access, i_session_id ); |
1795 | else |
1796 | msg_Err( p_access, "unsupported MMI mode %02x", d[1] ); |
1797 | break; |
1798 | |
1799 | default: |
1800 | msg_Err( p_access, "unsupported display control command %02x", |
1801 | *d ); |
1802 | break; |
1803 | } |
1804 | } |
1805 | break; |
1806 | } |
1807 | |
1808 | case AOT_ENQ0x9F8807: |
1809 | MMIHandleEnq( p_access, i_session_id, p_apdu, i_size ); |
1810 | break; |
1811 | |
1812 | case AOT_LIST_LAST0x9F880C: |
1813 | case AOT_MENU_LAST0x9F8809: |
1814 | MMIHandleMenu( p_access, i_session_id, i_tag, p_apdu, i_size ); |
1815 | break; |
1816 | |
1817 | case AOT_CLOSE_MMI0x9F8800: |
1818 | SessionSendClose( p_access, i_session_id ); |
1819 | break; |
1820 | |
1821 | default: |
1822 | msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag ); |
1823 | } |
1824 | } |
1825 | |
1826 | /***************************************************************************** |
1827 | * MMIClose |
1828 | *****************************************************************************/ |
1829 | static void MMIClose( access_t *p_access, int i_session_id ) |
1830 | { |
1831 | int i_slot = p_sessions[i_session_id - 1].i_slot; |
1832 | mmi_t *p_mmi = (mmi_t *)p_sessions[i_session_id - 1].p_sys; |
1833 | |
1834 | en50221_MMIFree( &p_mmi->last_object ); |
1835 | free( p_sessions[i_session_id - 1].p_sys ); |
1836 | |
1837 | msg_Dbg( p_access, "closing MMI session (%d)", i_session_id ); |
1838 | |
1839 | p_slots[i_slot].b_mmi_expected = false0; |
1840 | p_slots[i_slot].b_mmi_undisplayed = true1; |
1841 | } |
1842 | |
1843 | /***************************************************************************** |
1844 | * MMIOpen |
1845 | *****************************************************************************/ |
1846 | static void MMIOpen( access_t *p_access, int i_session_id ) |
1847 | { |
1848 | mmi_t *p_mmi; |
1849 | |
1850 | msg_Dbg( p_access, "opening MMI session (%d)", i_session_id ); |
1851 | |
1852 | p_sessions[i_session_id - 1].pf_handle = MMIHandle; |
1853 | p_sessions[i_session_id - 1].pf_close = MMIClose; |
1854 | p_sessions[i_session_id - 1].p_sys = malloc(sizeof(mmi_t)); |
1855 | p_mmi = (mmi_t *)p_sessions[i_session_id - 1].p_sys; |
1856 | p_mmi->last_object.i_object_type = EN50221_MMI_NONE0; |
1857 | } |
1858 | |
1859 | |
1860 | /* |
1861 | * Hardware handling |
1862 | */ |
1863 | |
1864 | /***************************************************************************** |
1865 | * InitSlot: Open the transport layer |
1866 | *****************************************************************************/ |
1867 | static void InitSlot( access_t * p_access, int i_slot ) |
1868 | { |
1869 | if ( TPDUSend( p_access, i_slot, T_CREATE_TC0x82, NULL((void*)0), 0 ) != 0 ) |
1870 | msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d", |
1871 | i_slot ); |
1872 | } |
1873 | |
1874 | /***************************************************************************** |
1875 | * ResetSlot |
1876 | *****************************************************************************/ |
1877 | static void ResetSlot( int i_slot ) |
1878 | { |
1879 | ci_slot_t *p_slot = &p_slots[i_slot]; |
1880 | int i_session_id; |
1881 | |
1882 | switch (i_print_type) |
1883 | { |
1884 | case PRINT_XML: |
1885 | printf("<STATUS type=\"cam\" status=\"0\" />\n"); |
1886 | break; |
1887 | default: |
1888 | break; |
1889 | } |
1890 | |
1891 | if ( ioctl( i_ca_handle, CA_RESET(((0U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((128)) << 0) | ((0) << ((0 +8)+8))), 1 << i_slot ) != 0 ) |
1892 | msg_Err( NULL((void*)0), "en50221_Poll: couldn't reset slot %d", i_slot ); |
1893 | p_slot->b_active = false0; |
1894 | p_slot->i_init_timeout = mdate() + CAM_INIT_TIMEOUT15000000; |
1895 | p_slot->b_expect_answer = false0; |
1896 | p_slot->b_mmi_expected = false0; |
1897 | p_slot->b_mmi_undisplayed = false0; |
1898 | if ( p_slot->p_recv != NULL((void*)0) ) |
1899 | { |
1900 | free( p_slot->p_recv->p_data ); |
1901 | free( p_slot->p_recv ); |
1902 | } |
1903 | p_slot->p_recv = NULL((void*)0); |
1904 | while ( p_slot->p_send != NULL((void*)0) ) |
1905 | { |
1906 | en50221_msg_t *p_next = p_slot->p_send->p_next; |
1907 | free( p_slot->p_send->p_data ); |
1908 | free( p_slot->p_send ); |
1909 | p_slot->p_send = p_next; |
1910 | } |
1911 | p_slot->pp_send_last = &p_slot->p_send; |
1912 | |
1913 | /* Close all sessions for this slot. */ |
1914 | for ( i_session_id = 1; i_session_id <= MAX_SESSIONS32; i_session_id++ ) |
1915 | { |
1916 | if ( p_sessions[i_session_id - 1].i_resource_id |
1917 | && p_sessions[i_session_id - 1].i_slot == i_slot ) |
1918 | { |
1919 | if ( p_sessions[i_session_id - 1].pf_close != NULL((void*)0) ) |
1920 | { |
1921 | p_sessions[i_session_id - 1].pf_close( NULL((void*)0), i_session_id ); |
1922 | } |
1923 | p_sessions[i_session_id - 1].i_resource_id = 0; |
1924 | } |
1925 | } |
1926 | } |
1927 | |
1928 | |
1929 | /* |
1930 | * External entry points |
1931 | */ |
1932 | |
1933 | /***************************************************************************** |
1934 | * en50221_Init : Initialize the CAM for en50221 |
1935 | *****************************************************************************/ |
1936 | void en50221_Init( void ) |
1937 | { |
1938 | char psz_tmp[128]; |
1939 | ca_caps_t caps; |
1940 | |
1941 | memset( &caps, 0, sizeof( ca_caps_t )); |
1942 | |
1943 | sprintf( psz_tmp, "/dev/dvb/adapter%d/ca%d", i_adapter, i_canum ); |
1944 | if( (i_ca_handle = open(psz_tmp, O_RDWR02 | O_NONBLOCK04000)) < 0 ) |
1945 | { |
1946 | msg_Warn( NULL((void*)0), "failed opening CAM device %s (%s)", |
1947 | psz_tmp, strerror(errno(*__errno_location ())) ); |
1948 | i_ca_handle = 0; |
1949 | return; |
1950 | } |
1951 | |
1952 | if ( ioctl( i_ca_handle, CA_GET_CAP(((2U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((129)) << 0) | ((((sizeof(ca_caps_t)))) << ((0 +8)+8))), &caps ) != 0 ) |
1953 | { |
1954 | msg_Err( NULL((void*)0), "failed getting CAM capabilities (%s)", |
1955 | strerror(errno(*__errno_location ())) ); |
1956 | close( i_ca_handle ); |
1957 | i_ca_handle = 0; |
1958 | return; |
1959 | } |
1960 | |
1961 | /* Output CA capabilities */ |
1962 | msg_Dbg( NULL((void*)0), "CA interface with %d %s", caps.slot_num, |
1963 | caps.slot_num == 1 ? "slot" : "slots" ); |
1964 | if ( caps.slot_type & CA_CI1 ) |
1965 | msg_Dbg( NULL((void*)0), " CI high level interface type" ); |
1966 | if ( caps.slot_type & CA_CI_LINK2 ) |
1967 | msg_Dbg( NULL((void*)0), " CI link layer level interface type" ); |
1968 | if ( caps.slot_type & CA_CI_PHYS4 ) |
1969 | msg_Dbg( NULL((void*)0), " CI physical layer level interface type (not supported) " ); |
1970 | if ( caps.slot_type & CA_DESCR8 ) |
1971 | msg_Dbg( NULL((void*)0), " built-in descrambler detected" ); |
1972 | if ( caps.slot_type & CA_SC128 ) |
1973 | msg_Dbg( NULL((void*)0), " simple smart card interface" ); |
1974 | |
1975 | msg_Dbg( NULL((void*)0), " %d available %s", caps.descr_num, |
1976 | caps.descr_num == 1 ? "descrambler (key)" : "descramblers (keys)" ); |
1977 | if ( caps.descr_type & CA_ECD1 ) |
1978 | msg_Dbg( NULL((void*)0), " ECD scrambling system supported" ); |
1979 | if ( caps.descr_type & CA_NDS2 ) |
1980 | msg_Dbg( NULL((void*)0), " NDS scrambling system supported" ); |
1981 | if ( caps.descr_type & CA_DSS4 ) |
1982 | msg_Dbg( NULL((void*)0), " DSS scrambling system supported" ); |
1983 | |
1984 | if ( caps.slot_num == 0 ) |
1985 | { |
1986 | msg_Err( NULL((void*)0), "CAM module with no slots" ); |
1987 | close( i_ca_handle ); |
1988 | i_ca_handle = 0; |
1989 | return; |
1990 | } |
1991 | |
1992 | if( caps.slot_type & CA_CI_LINK2 ) |
1993 | i_ca_type = CA_CI_LINK2; |
1994 | else if( caps.slot_type & CA_CI1 ) |
1995 | i_ca_type = CA_CI1; |
1996 | else |
1997 | { |
1998 | msg_Err( NULL((void*)0), "Incompatible CAM interface" ); |
1999 | close( i_ca_handle ); |
2000 | i_ca_handle = 0; |
2001 | return; |
2002 | } |
2003 | |
2004 | i_nb_slots = caps.slot_num; |
2005 | memset( p_sessions, 0, sizeof(en50221_session_t) * MAX_SESSIONS32 ); |
2006 | |
2007 | en50221_Reset(); |
2008 | } |
2009 | |
2010 | /***************************************************************************** |
2011 | * en50221_Reset : Reset the CAM for en50221 |
2012 | *****************************************************************************/ |
2013 | void en50221_Reset( void ) |
2014 | { |
2015 | memset( p_slots, 0, sizeof(ci_slot_t) * MAX_CI_SLOTS16 ); |
2016 | |
2017 | if( i_ca_type & CA_CI_LINK2 ) |
2018 | { |
2019 | int i_slot; |
2020 | for ( i_slot = 0; i_slot < i_nb_slots; i_slot++ ) |
2021 | ResetSlot( i_slot ); |
2022 | } |
2023 | else |
2024 | { |
2025 | struct ca_slot_info info; |
2026 | system_ids_t *p_ids; |
2027 | ca_msg_t ca_msg; |
2028 | info.num = 0; |
2029 | |
2030 | /* We don't reset the CAM in that case because it's done by the |
2031 | * ASIC. */ |
2032 | if ( ioctl( i_ca_handle, CA_GET_SLOT_INFO(((2U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((130)) << 0) | ((((sizeof(ca_slot_info_t)))) << ((0 +8)+8))), &info ) < 0 ) |
2033 | { |
2034 | msg_Err( NULL((void*)0), "en50221_Init: couldn't get slot info" ); |
2035 | close( i_ca_handle ); |
2036 | i_ca_handle = 0; |
2037 | return; |
2038 | } |
2039 | if( info.flags == 0 ) |
2040 | { |
2041 | msg_Err( NULL((void*)0), "en50221_Init: no CAM inserted" ); |
2042 | close( i_ca_handle ); |
2043 | i_ca_handle = 0; |
2044 | return; |
2045 | } |
2046 | |
2047 | /* Allocate a dummy sessions */ |
2048 | p_sessions[0].i_resource_id = RI_CONDITIONAL_ACCESS_SUPPORT0x00030041; |
2049 | p_sessions[0].pf_close = ConditionalAccessClose; |
2050 | if ( p_sessions[0].p_sys == NULL((void*)0) ) |
2051 | p_sessions[0].p_sys = malloc(sizeof(system_ids_t)); |
2052 | memset( p_sessions[0].p_sys, 0, sizeof(system_ids_t) ); |
2053 | p_ids = (system_ids_t *)p_sessions[0].p_sys; |
2054 | p_ids->b_high_level = 1; |
2055 | |
2056 | /* Get application info to find out which cam we are using and make |
2057 | sure everything is ready to play */ |
2058 | ca_msg.length=3; |
2059 | ca_msg.msg[0] = ( AOT_APPLICATION_INFO0x9F8021 & 0xFF0000 ) >> 16; |
2060 | ca_msg.msg[1] = ( AOT_APPLICATION_INFO0x9F8021 & 0x00FF00 ) >> 8; |
2061 | ca_msg.msg[2] = ( AOT_APPLICATION_INFO0x9F8021 & 0x0000FF ) >> 0; |
2062 | memset( &ca_msg.msg[3], 0, 253 ); |
2063 | APDUSend( NULL((void*)0), 1, AOT_APPLICATION_INFO_ENQ0x9F8020, NULL((void*)0), 0 ); |
2064 | if ( ioctl( i_ca_handle, CA_GET_MSG(((2U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((132)) << 0) | ((((sizeof(ca_msg_t)))) << ((0 + 8)+8))), &ca_msg ) < 0 ) |
2065 | { |
2066 | msg_Err( NULL((void*)0), "en50221_Init: failed getting message" ); |
2067 | close( i_ca_handle ); |
2068 | i_ca_handle = 0; |
2069 | return; |
2070 | } |
2071 | |
2072 | #ifdef HLCI_WAIT_CAM_READY |
2073 | while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff ) |
2074 | { |
2075 | msleep(1); |
2076 | msg_Dbg( NULL((void*)0), "CAM: please wait" ); |
2077 | APDUSend( NULL((void*)0), 1, AOT_APPLICATION_INFO_ENQ0x9F8020, NULL((void*)0), 0 ); |
2078 | ca_msg.length=3; |
2079 | ca_msg.msg[0] = ( AOT_APPLICATION_INFO0x9F8021 & 0xFF0000 ) >> 16; |
2080 | ca_msg.msg[1] = ( AOT_APPLICATION_INFO0x9F8021 & 0x00FF00 ) >> 8; |
2081 | ca_msg.msg[2] = ( AOT_APPLICATION_INFO0x9F8021 & 0x0000FF ) >> 0; |
2082 | memset( &ca_msg.msg[3], 0, 253 ); |
2083 | if ( ioctl( i_ca_handle, CA_GET_MSG(((2U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((132)) << 0) | ((((sizeof(ca_msg_t)))) << ((0 + 8)+8))), &ca_msg ) < 0 ) |
2084 | { |
2085 | msg_Err( NULL((void*)0), "en50221_Init: failed getting message" ); |
2086 | close( i_ca_handle ); |
2087 | i_ca_handle = 0; |
2088 | return; |
2089 | } |
2090 | msg_Dbg( NULL((void*)0), "en50221_Init: Got length: %d, tag: 0x%x", ca_msg.length, APDUGetTag( ca_msg.msg, ca_msg.length ) ); |
2091 | } |
2092 | #else |
2093 | if( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff ) |
2094 | { |
2095 | msg_Err( NULL((void*)0), "CAM returns garbage as application info!" ); |
2096 | close( i_ca_handle ); |
2097 | i_ca_handle = 0; |
2098 | return; |
2099 | } |
2100 | #endif |
2101 | msg_Dbg( NULL((void*)0), "found CAM %s using id 0x%x", &ca_msg.msg[12], |
2102 | (ca_msg.msg[8]<<8)|ca_msg.msg[9] ); |
2103 | } |
2104 | } |
2105 | |
2106 | /***************************************************************************** |
2107 | * en50221_Read : Read the CAM for a TPDU |
2108 | *****************************************************************************/ |
2109 | void en50221_Read( void ) |
2110 | { |
2111 | TPDURecv( NULL((void*)0) ); |
2112 | } |
2113 | |
2114 | /***************************************************************************** |
2115 | * en50221_Poll : Send a poll TPDU to the CAM |
2116 | *****************************************************************************/ |
2117 | void en50221_Poll( void ) |
2118 | { |
2119 | int i_slot; |
2120 | int i_session_id; |
2121 | |
2122 | /* Check module status */ |
2123 | for ( i_slot = 0; i_slot < i_nb_slots; i_slot++ ) |
2124 | { |
2125 | ci_slot_t *p_slot = &p_slots[i_slot]; |
2126 | ca_slot_info_t sinfo; |
2127 | |
2128 | sinfo.num = i_slot; |
2129 | if ( ioctl( i_ca_handle, CA_GET_SLOT_INFO(((2U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((130)) << 0) | ((((sizeof(ca_slot_info_t)))) << ((0 +8)+8))), &sinfo ) != 0 ) |
2130 | { |
2131 | msg_Err( NULL((void*)0), "en50221_Poll: couldn't get info on slot %d", |
2132 | i_slot ); |
2133 | continue; |
2134 | } |
2135 | |
2136 | if ( !(sinfo.flags & CA_CI_MODULE_READY2) ) |
2137 | { |
2138 | if ( p_slot->b_active ) |
2139 | { |
2140 | msg_Dbg( NULL((void*)0), "en50221_Poll: slot %d has been removed", |
2141 | i_slot ); |
2142 | ResetSlot( i_slot ); |
2143 | } |
2144 | |
2145 | continue; |
2146 | } |
2147 | else if ( !p_slot->b_active ) |
2148 | { |
2149 | if ( !p_slot->b_expect_answer ) |
2150 | InitSlot( NULL((void*)0), i_slot ); |
2151 | else if ( p_slot->i_init_timeout < i_wallclock ) |
2152 | { |
2153 | switch (i_print_type) { |
2154 | case PRINT_XML: |
2155 | printf("<EVENT type=\"reset\" cause=\"cam_mute\" />\n"); |
2156 | break; |
2157 | default: |
2158 | msg_Warn( NULL((void*)0), "no answer from CAM, resetting slot %d", |
2159 | i_slot ); |
2160 | } |
2161 | ResetSlot( i_slot ); |
2162 | continue; |
2163 | } |
2164 | } |
2165 | } |
2166 | |
2167 | /* Check if applications have data to send */ |
2168 | for ( i_session_id = 1; i_session_id <= MAX_SESSIONS32; i_session_id++ ) |
2169 | { |
2170 | en50221_session_t *p_session = &p_sessions[i_session_id - 1]; |
2171 | if ( p_session->i_resource_id && p_session->pf_manage != NULL((void*)0) |
2172 | && !p_slots[ p_session->i_slot ].b_expect_answer ) |
2173 | p_session->pf_manage( NULL((void*)0), i_session_id ); |
2174 | } |
2175 | |
2176 | /* Now send the poll command to inactive slots */ |
2177 | for ( i_slot = 0; i_slot < i_nb_slots; i_slot++ ) |
2178 | { |
2179 | ci_slot_t *p_slot = &p_slots[i_slot]; |
2180 | |
2181 | if ( p_slot->b_active && !p_slot->b_expect_answer ) |
2182 | { |
2183 | if ( TPDUSend( NULL((void*)0), i_slot, T_DATA_LAST0xA0, NULL((void*)0), 0 ) != 0 ) |
2184 | { |
2185 | switch (i_print_type) { |
2186 | case PRINT_XML: |
2187 | printf("<EVENT type=\"reset\" cause=\"cam_error\" />\n"); |
2188 | break; |
2189 | default: |
2190 | msg_Warn( NULL((void*)0), "couldn't send TPDU, resetting slot %d", |
2191 | i_slot ); |
2192 | } |
2193 | ResetSlot( i_slot ); |
2194 | } |
2195 | } |
2196 | } |
2197 | } |
2198 | |
2199 | /***************************************************************************** |
2200 | * en50221_AddPMT : |
2201 | *****************************************************************************/ |
2202 | void en50221_AddPMT( uint8_t *p_pmt ) |
2203 | { |
2204 | int i_session_id; |
2205 | |
2206 | for ( i_session_id = 1; i_session_id <= MAX_SESSIONS32; i_session_id++ ) |
2207 | if ( p_sessions[i_session_id - 1].i_resource_id |
2208 | == RI_CONDITIONAL_ACCESS_SUPPORT0x00030041 ) |
2209 | CAPMTAdd( NULL((void*)0), i_session_id, p_pmt ); |
2210 | } |
2211 | |
2212 | /***************************************************************************** |
2213 | * en50221_UpdatePMT : |
2214 | *****************************************************************************/ |
2215 | void en50221_UpdatePMT( uint8_t *p_pmt ) |
2216 | { |
2217 | int i_session_id; |
2218 | |
2219 | for ( i_session_id = 1; i_session_id <= MAX_SESSIONS32; i_session_id++ ) |
2220 | if ( p_sessions[i_session_id - 1].i_resource_id |
2221 | == RI_CONDITIONAL_ACCESS_SUPPORT0x00030041 ) |
2222 | CAPMTUpdate( NULL((void*)0), i_session_id, p_pmt ); |
2223 | } |
2224 | |
2225 | /***************************************************************************** |
2226 | * en50221_DeletePMT : |
2227 | *****************************************************************************/ |
2228 | void en50221_DeletePMT( uint8_t *p_pmt ) |
2229 | { |
2230 | int i_session_id; |
2231 | |
2232 | for ( i_session_id = 1; i_session_id <= MAX_SESSIONS32; i_session_id++ ) |
2233 | if ( p_sessions[i_session_id - 1].i_resource_id |
2234 | == RI_CONDITIONAL_ACCESS_SUPPORT0x00030041 ) |
2235 | CAPMTDelete( NULL((void*)0), i_session_id, p_pmt ); |
2236 | } |
2237 | |
2238 | /***************************************************************************** |
2239 | * en50221_StatusMMI : |
2240 | *****************************************************************************/ |
2241 | uint8_t en50221_StatusMMI( uint8_t *p_answer, ssize_t *pi_size ) |
2242 | { |
2243 | struct ret_mmi_status *p_ret = (struct ret_mmi_status *)p_answer; |
2244 | |
2245 | if ( ioctl( i_ca_handle, CA_GET_CAP(((2U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((129)) << 0) | ((((sizeof(ca_caps_t)))) << ((0 +8)+8))), &p_ret->caps ) != 0 ) |
2246 | { |
2247 | msg_Err( NULL((void*)0), "ioctl CA_GET_CAP failed (%s)", strerror(errno(*__errno_location ())) ); |
2248 | return RET_ERR; |
2249 | } |
2250 | |
2251 | *pi_size = sizeof(struct ret_mmi_status); |
2252 | return RET_MMI_STATUS; |
2253 | } |
2254 | |
2255 | /***************************************************************************** |
2256 | * en50221_StatusMMISlot : |
2257 | *****************************************************************************/ |
2258 | uint8_t en50221_StatusMMISlot( uint8_t *p_buffer, ssize_t i_size, |
2259 | uint8_t *p_answer, ssize_t *pi_size ) |
2260 | { |
2261 | int i_slot; |
2262 | struct ret_mmi_slot_status *p_ret = (struct ret_mmi_slot_status *)p_answer; |
2263 | |
2264 | if ( i_size != 1 ) return RET_HUH; |
2265 | i_slot = *p_buffer; |
2266 | |
2267 | p_ret->sinfo.num = i_slot; |
2268 | if ( ioctl( i_ca_handle, CA_GET_SLOT_INFO(((2U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((130)) << 0) | ((((sizeof(ca_slot_info_t)))) << ((0 +8)+8))), &p_ret->sinfo ) != 0 ) |
2269 | { |
2270 | msg_Err( NULL((void*)0), "ioctl CA_GET_SLOT_INFO failed (%s)", strerror(errno(*__errno_location ())) ); |
2271 | return RET_ERR; |
2272 | } |
2273 | |
2274 | *pi_size = sizeof(struct ret_mmi_slot_status); |
2275 | return RET_MMI_SLOT_STATUS; |
2276 | } |
2277 | |
2278 | /***************************************************************************** |
2279 | * en50221_OpenMMI : |
2280 | *****************************************************************************/ |
2281 | uint8_t en50221_OpenMMI( uint8_t *p_buffer, ssize_t i_size ) |
2282 | { |
2283 | int i_slot; |
2284 | |
2285 | if ( i_size != 1 ) return RET_HUH; |
2286 | i_slot = *p_buffer; |
2287 | |
2288 | if( i_ca_type & CA_CI_LINK2 ) |
2289 | { |
2290 | int i_session_id; |
2291 | for ( i_session_id = 1; i_session_id <= MAX_SESSIONS32; i_session_id++ ) |
2292 | { |
2293 | if ( p_sessions[i_session_id - 1].i_resource_id == RI_MMI0x00400041 |
2294 | && p_sessions[i_session_id - 1].i_slot == i_slot ) |
2295 | { |
2296 | msg_Dbg( NULL((void*)0), |
2297 | "MMI menu is already opened on slot %d (session=%d)", |
2298 | i_slot, i_session_id ); |
2299 | return RET_OK; |
2300 | } |
2301 | } |
2302 | |
2303 | for ( i_session_id = 1; i_session_id <= MAX_SESSIONS32; i_session_id++ ) |
2304 | { |
2305 | if ( p_sessions[i_session_id - 1].i_resource_id |
2306 | == RI_APPLICATION_INFORMATION0x00020041 |
2307 | && p_sessions[i_session_id - 1].i_slot == i_slot ) |
2308 | { |
2309 | ApplicationInformationEnterMenu( NULL((void*)0), i_session_id ); |
2310 | return RET_OK; |
2311 | } |
2312 | } |
2313 | |
2314 | msg_Err( NULL((void*)0), "no application information on slot %d", i_slot ); |
2315 | return RET_ERR; |
2316 | } |
2317 | else |
2318 | { |
2319 | msg_Err( NULL((void*)0), "MMI menu not supported" ); |
2320 | return RET_ERR; |
2321 | } |
2322 | } |
2323 | |
2324 | /***************************************************************************** |
2325 | * en50221_CloseMMI : |
2326 | *****************************************************************************/ |
2327 | uint8_t en50221_CloseMMI( uint8_t *p_buffer, ssize_t i_size ) |
2328 | { |
2329 | int i_slot; |
2330 | |
2331 | if ( i_size != 1 ) return RET_HUH; |
2332 | i_slot = *p_buffer; |
2333 | |
2334 | if( i_ca_type & CA_CI_LINK2 ) |
2335 | { |
2336 | int i_session_id; |
2337 | for ( i_session_id = 1; i_session_id <= MAX_SESSIONS32; i_session_id++ ) |
2338 | { |
2339 | if ( p_sessions[i_session_id - 1].i_resource_id == RI_MMI0x00400041 |
2340 | && p_sessions[i_session_id - 1].i_slot == i_slot ) |
2341 | { |
2342 | MMISendClose( NULL((void*)0), i_session_id ); |
2343 | return RET_OK; |
2344 | } |
2345 | } |
2346 | |
2347 | msg_Warn( NULL((void*)0), "closing a non-existing MMI session on slot %d", |
2348 | i_slot ); |
2349 | return RET_ERR; |
2350 | } |
2351 | else |
2352 | { |
2353 | msg_Err( NULL((void*)0), "MMI menu not supported" ); |
2354 | return RET_ERR; |
2355 | } |
2356 | } |
2357 | |
2358 | /***************************************************************************** |
2359 | * en50221_GetMMIObject : |
2360 | *****************************************************************************/ |
2361 | uint8_t en50221_GetMMIObject( uint8_t *p_buffer, ssize_t i_size, |
2362 | uint8_t *p_answer, ssize_t *pi_size ) |
2363 | { |
2364 | int i_session_id, i_slot; |
2365 | struct ret_mmi_recv *p_ret = (struct ret_mmi_recv *)p_answer; |
2366 | |
2367 | if ( i_size != 1 ) return RET_HUH; |
2368 | i_slot = *p_buffer; |
2369 | |
2370 | if ( p_slots[i_slot].b_mmi_expected ) |
2371 | return RET_MMI_WAIT; /* data not yet available */ |
2372 | |
2373 | p_ret->object.i_object_type = EN50221_MMI_NONE0; |
2374 | *pi_size = sizeof(struct ret_mmi_recv); |
2375 | |
2376 | for ( i_session_id = 1; i_session_id <= MAX_SESSIONS32; i_session_id++ ) |
2377 | { |
2378 | if ( p_sessions[i_session_id - 1].i_resource_id == RI_MMI0x00400041 |
2379 | && p_sessions[i_session_id - 1].i_slot == i_slot ) |
2380 | { |
2381 | mmi_t *p_mmi = |
2382 | (mmi_t *)p_sessions[i_session_id - 1].p_sys; |
2383 | if ( p_mmi == NULL((void*)0) ) |
2384 | { |
2385 | *pi_size = 0; |
2386 | return RET_ERR; /* should not happen */ |
2387 | } |
2388 | |
2389 | *pi_size = COMM_BUFFER_SIZE(8 + ((4093 + 3) * (256 / 2))) - COMM_HEADER_SIZE8 - |
2390 | ((void *)&p_ret->object - (void *)p_ret); |
2391 | if ( en50221_SerializeMMIObject( (uint8_t *)&p_ret->object, |
2392 | pi_size, &p_mmi->last_object ) == -1 ) |
2393 | { |
2394 | *pi_size = 0; |
2395 | msg_Err( NULL((void*)0), "MMI structure too big" ); |
2396 | return RET_ERR; |
2397 | } |
2398 | *pi_size += ((void *)&p_ret->object - (void *)p_ret); |
2399 | break; |
2400 | } |
2401 | } |
2402 | |
2403 | return RET_MMI_RECV; |
2404 | } |
2405 | |
2406 | |
2407 | /***************************************************************************** |
2408 | * en50221_SendMMIObject : |
2409 | *****************************************************************************/ |
2410 | uint8_t en50221_SendMMIObject( uint8_t *p_buffer, ssize_t i_size ) |
2411 | { |
2412 | int i_session_id, i_slot; |
2413 | struct cmd_mmi_send *p_cmd = (struct cmd_mmi_send *)p_buffer; |
2414 | |
2415 | if ( i_size < sizeof(struct cmd_mmi_send)) |
2416 | { |
2417 | msg_Err( NULL((void*)0), "command packet too short (%zd)\n", i_size ); |
2418 | return RET_HUH; |
2419 | } |
2420 | |
2421 | if ( en50221_UnserializeMMIObject( &p_cmd->object, i_size - |
2422 | ((void *)&p_cmd->object - (void *)p_cmd) ) == -1 ) |
2423 | return RET_ERR; |
2424 | |
2425 | i_slot = p_cmd->i_slot; |
2426 | |
2427 | for ( i_session_id = 1; i_session_id <= MAX_SESSIONS32; i_session_id++ ) |
2428 | { |
2429 | if ( p_sessions[i_session_id - 1].i_resource_id == RI_MMI0x00400041 |
2430 | && p_sessions[i_session_id - 1].i_slot == i_slot ) |
2431 | { |
2432 | MMISendObject( NULL((void*)0), i_session_id, &p_cmd->object ); |
2433 | return RET_OK; |
2434 | } |
2435 | } |
2436 | |
2437 | msg_Err( NULL((void*)0), "SendMMIObject when no MMI session is opened !" ); |
2438 | return RET_ERR; |
2439 | } |
2440 | |
2441 | #else |
2442 | #include <inttypes.h> |
2443 | |
2444 | int i_ca_handle = 0; |
2445 | int i_ca_type = -1; |
2446 | |
2447 | void en50221_AddPMT( uint8_t *p_pmt ) { }; |
2448 | void en50221_UpdatePMT( uint8_t *p_pmt ) { }; |
2449 | void en50221_DeletePMT( uint8_t *p_pmt ) { }; |
2450 | void en50221_Reset( void ) { }; |
2451 | #endif |