]> git.sesse.net Git - vlc/blob - modules/access/dvb/en50221.c
* modules/access/dvb: Fixed link-level CAM API.
[vlc] / modules / access / dvb / en50221.c
1 /*****************************************************************************
2  * en50221.c : implementation of the transport, session and applications
3  * layers of EN 50 221
4  *****************************************************************************
5  * Copyright (C) 2004 the VideoLAN team
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., 59 Temple Place - Suite 330, Boston, MA    02111, USA.
23  *****************************************************************************/
24
25 #include <vlc/vlc.h>
26 #include <vlc/input.h>
27
28 #include <sys/ioctl.h>
29 #include <errno.h>
30
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <time.h>
35 #include <unistd.h>
36 #include <sys/stat.h>
37 #include <sys/poll.h>
38
39 /* DVB Card Drivers */
40 #include <linux/dvb/version.h>
41 #include <linux/dvb/dmx.h>
42 #include <linux/dvb/frontend.h>
43 #include <linux/dvb/ca.h>
44
45 /* Include dvbpsi headers */
46 #ifdef HAVE_DVBPSI_DR_H
47 #   include <dvbpsi/dvbpsi.h>
48 #   include <dvbpsi/descriptor.h>
49 #   include <dvbpsi/pat.h>
50 #   include <dvbpsi/pmt.h>
51 #   include <dvbpsi/dr.h>
52 #   include <dvbpsi/psi.h>
53 #else
54 #   include "dvbpsi.h"
55 #   include "descriptor.h"
56 #   include "tables/pat.h"
57 #   include "tables/pmt.h"
58 #   include "descriptors/dr.h"
59 #   include "psi.h"
60 #endif
61
62 #include "dvb.h"
63
64 #undef DEBUG_TPDU
65 #define HLCI_WAIT_CAM_READY 0
66 #define CAM_PROG_MAX MAX_PROGRAMS
67
68 static void ResourceManagerOpen( access_t * p_access, int i_session_id );
69 static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
70 static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
71 static void DateTimeOpen( access_t * p_access, int i_session_id );
72 static void MMIOpen( access_t * p_access, int i_session_id );
73
74 /*****************************************************************************
75  * Utility functions
76  *****************************************************************************/
77 #define SIZE_INDICATOR 0x80
78
79 static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
80 {
81     *pi_length = *p_data++;
82
83     if ( (*pi_length & SIZE_INDICATOR) != 0 )
84     {
85         int l = *pi_length & ~SIZE_INDICATOR;
86         int i;
87
88         *pi_length = 0;
89         for ( i = 0; i < l; i++ )
90             *pi_length = (*pi_length << 8) | *p_data++;
91     }
92
93     return p_data;
94 }
95
96 static uint8_t *SetLength( uint8_t *p_data, int i_length )
97 {
98     uint8_t *p = p_data;
99
100     if ( i_length < 128 )
101     {
102         *p++ = i_length;
103     }
104     else if ( i_length < 256 )
105     {
106         *p++ = SIZE_INDICATOR | 0x1;
107         *p++ = i_length;
108     }
109     else if ( i_length < 65536 )
110     {
111         *p++ = SIZE_INDICATOR | 0x2;
112         *p++ = i_length >> 8;
113         *p++ = i_length & 0xff;
114     }
115     else if ( i_length < 16777216 )
116     {
117         *p++ = SIZE_INDICATOR | 0x3;
118         *p++ = i_length >> 16;
119         *p++ = (i_length >> 8) & 0xff;
120         *p++ = i_length & 0xff;
121     }
122     else
123     {
124         *p++ = SIZE_INDICATOR | 0x4;
125         *p++ = i_length >> 24;
126         *p++ = (i_length >> 16) & 0xff;
127         *p++ = (i_length >> 8) & 0xff;
128         *p++ = i_length & 0xff;
129     }
130
131     return p;
132 }
133
134
135 /*
136  * Transport layer
137  */
138
139 #define MAX_TPDU_SIZE  2048
140 #define MAX_TPDU_DATA  (MAX_TPDU_SIZE - 4)
141
142 #define DATA_INDICATOR 0x80
143
144 #define T_SB           0x80
145 #define T_RCV          0x81
146 #define T_CREATE_TC    0x82
147 #define T_CTC_REPLY    0x83
148 #define T_DELETE_TC    0x84
149 #define T_DTC_REPLY    0x85
150 #define T_REQUEST_TC   0x86
151 #define T_NEW_TC       0x87
152 #define T_TC_ERROR     0x88
153 #define T_DATA_LAST    0xA0
154 #define T_DATA_MORE    0xA1
155
156 static void Dump( vlc_bool_t b_outgoing, uint8_t *p_data, int i_size )
157 {
158 #ifdef DEBUG_TPDU
159     int i;
160 #define MAX_DUMP 256
161     fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
162     for ( i = 0; i < i_size && i < MAX_DUMP; i++)
163         fprintf(stderr, "%02X ", p_data[i]);
164     fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
165 #endif
166 }
167
168 /*****************************************************************************
169  * TPDUSend
170  *****************************************************************************/
171 static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
172                      const uint8_t *p_content, int i_length )
173 {
174     access_sys_t *p_sys = p_access->p_sys;
175     uint8_t i_tcid = i_slot + 1;
176     uint8_t p_data[MAX_TPDU_SIZE];
177     int i_size;
178
179     i_size = 0;
180     p_data[0] = i_slot;
181     p_data[1] = i_tcid;
182     p_data[2] = i_tag;
183
184     switch ( i_tag )
185     {
186     case T_RCV:
187     case T_CREATE_TC:
188     case T_CTC_REPLY:
189     case T_DELETE_TC:
190     case T_DTC_REPLY:
191     case T_REQUEST_TC:
192         p_data[3] = 1; /* length */
193         p_data[4] = i_tcid;
194         i_size = 5;
195         break;
196
197     case T_NEW_TC:
198     case T_TC_ERROR:
199         p_data[3] = 2; /* length */
200         p_data[4] = i_tcid;
201         p_data[5] = p_content[0];
202         i_size = 6;
203         break;
204
205     case T_DATA_LAST:
206     case T_DATA_MORE:
207     {
208         /* i_length <= MAX_TPDU_DATA */
209         uint8_t *p = p_data + 3;
210         p = SetLength( p, i_length + 1 );
211         *p++ = i_tcid;
212
213         if ( i_length )
214             memcpy( p, p_content, i_length );
215             i_size = i_length + (p - p_data);
216         }
217         break;
218
219     default:
220         break;
221     }
222     Dump( VLC_TRUE, p_data, i_size );
223
224     if ( write( p_sys->i_ca_handle, p_data, i_size ) != i_size )
225     {
226         msg_Err( p_access, "cannot write to CAM device (%s)",
227                  strerror(errno) );
228         return VLC_EGENERIC;
229     }
230
231     return VLC_SUCCESS;
232 }
233
234
235 /*****************************************************************************
236  * TPDURecv
237  *****************************************************************************/
238 #define CAM_READ_TIMEOUT  3500 // ms
239
240 static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
241                      uint8_t *p_data, int *pi_size )
242 {
243     access_sys_t *p_sys = p_access->p_sys;
244     uint8_t i_tcid = i_slot + 1;
245     int i_size;
246     struct pollfd pfd[1];
247
248     pfd[0].fd = p_sys->i_ca_handle;
249     pfd[0].events = POLLIN;
250     if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
251     {
252         msg_Err( p_access, "cannot poll from CAM device" );
253         return VLC_EGENERIC;
254     }
255
256     if ( pi_size == NULL )
257     {
258         p_data = malloc( MAX_TPDU_SIZE );
259     }
260
261     for ( ; ; )
262     {
263         i_size = read( p_sys->i_ca_handle, p_data, MAX_TPDU_SIZE );
264
265         if ( i_size >= 0 || errno != EINTR )
266             break;
267     }
268
269     if ( i_size < 5 )
270     {
271         msg_Err( p_access, "cannot read from CAM device (%d:%s)", i_size,
272                  strerror(errno) );
273         return VLC_EGENERIC;
274     }
275
276     if ( p_data[1] != i_tcid )
277     {
278         msg_Err( p_access, "invalid read from CAM device (%d instead of %d)",
279                  p_data[1], i_tcid );
280         return VLC_EGENERIC;
281     }
282
283     *pi_tag = p_data[2];
284     p_sys->pb_tc_has_data[i_slot] = (i_size >= 4
285                                       && p_data[i_size - 4] == T_SB
286                                       && p_data[i_size - 3] == 2
287                                       && (p_data[i_size - 1] & DATA_INDICATOR))
288                                         ?  VLC_TRUE : VLC_FALSE;
289
290     Dump( VLC_FALSE, p_data, i_size );
291
292     if ( pi_size == NULL )
293         free( p_data );
294     else
295         *pi_size = i_size;
296
297     return VLC_SUCCESS;
298 }
299
300
301 /*
302  * Session layer
303  */
304
305 #define ST_SESSION_NUMBER           0x90
306 #define ST_OPEN_SESSION_REQUEST     0x91
307 #define ST_OPEN_SESSION_RESPONSE    0x92
308 #define ST_CREATE_SESSION           0x93
309 #define ST_CREATE_SESSION_RESPONSE  0x94
310 #define ST_CLOSE_SESSION_REQUEST    0x95
311 #define ST_CLOSE_SESSION_RESPONSE   0x96
312
313 #define SS_OK             0x00
314 #define SS_NOT_ALLOCATED  0xF0
315
316 #define RI_RESOURCE_MANAGER            0x00010041
317 #define RI_APPLICATION_INFORMATION     0x00020041
318 #define RI_CONDITIONAL_ACCESS_SUPPORT  0x00030041
319 #define RI_HOST_CONTROL                0x00200041
320 #define RI_DATE_TIME                   0x00240041
321 #define RI_MMI                         0x00400041
322
323 static int ResourceIdToInt( uint8_t *p_data )
324 {
325     return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
326             | ((int)p_data[2] << 8) | p_data[3];
327 }
328
329 /*****************************************************************************
330  * SPDUSend
331  *****************************************************************************/
332 static int SPDUSend( access_t * p_access, int i_session_id,
333                      uint8_t *p_data, int i_size )
334 {
335     access_sys_t *p_sys = p_access->p_sys;
336     uint8_t *p_spdu = malloc( i_size + 4 );
337     uint8_t *p = p_spdu;
338     uint8_t i_tag;
339     uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
340
341     *p++ = ST_SESSION_NUMBER;
342     *p++ = 0x02;
343     *p++ = (i_session_id >> 8);
344     *p++ = i_session_id & 0xff;
345
346     memcpy( p, p_data, i_size );
347
348     i_size += 4;
349     p = p_spdu;
350
351     while ( i_size > 0 )
352     {
353         if ( i_size > MAX_TPDU_DATA )
354         {
355             if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
356                            MAX_TPDU_DATA ) != VLC_SUCCESS )
357             {
358                 msg_Err( p_access, "couldn't send TPDU on session %d",
359                          i_session_id );
360                 free( p_spdu );
361                 return VLC_EGENERIC;
362             }
363             p += MAX_TPDU_DATA;
364             i_size -= MAX_TPDU_DATA;
365         }
366         else
367         {
368             if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
369                     != VLC_SUCCESS )
370             {
371                 msg_Err( p_access, "couldn't send TPDU on session %d",
372                          i_session_id );
373                 free( p_spdu );
374                 return VLC_EGENERIC;
375             }
376             i_size = 0;
377         }
378
379         if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
380                || i_tag != T_SB )
381         {
382             msg_Err( p_access, "couldn't recv TPDU on session %d",
383                      i_session_id );
384             free( p_spdu );
385             return VLC_EGENERIC;
386         }
387     }
388
389     free( p_spdu );
390     return VLC_SUCCESS;
391 }
392
393 /*****************************************************************************
394  * SessionOpen
395  *****************************************************************************/
396 static void SessionOpen( access_t * p_access, uint8_t i_slot,
397                          uint8_t *p_spdu, int i_size )
398 {
399     access_sys_t *p_sys = p_access->p_sys;
400     int i_session_id;
401     int i_resource_id = ResourceIdToInt( &p_spdu[2] );
402     uint8_t p_response[16];
403     int i_status = SS_NOT_ALLOCATED;
404     uint8_t i_tag;
405
406     for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
407     {
408         if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
409             break;
410     }
411     if ( i_session_id == MAX_SESSIONS )
412     {
413         msg_Err( p_access, "too many sessions !" );
414         return;
415     }
416     p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
417     p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
418     p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
419     p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
420
421     if ( i_resource_id == RI_RESOURCE_MANAGER
422           || i_resource_id == RI_APPLICATION_INFORMATION
423           || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
424           || i_resource_id == RI_DATE_TIME
425           || i_resource_id == RI_MMI )
426     {
427         i_status = SS_OK;
428     }
429
430     p_response[0] = ST_OPEN_SESSION_RESPONSE;
431     p_response[1] = 0x7;
432     p_response[2] = i_status;
433     p_response[3] = p_spdu[2];
434     p_response[4] = p_spdu[3];
435     p_response[5] = p_spdu[4];
436     p_response[6] = p_spdu[5];
437     p_response[7] = i_session_id >> 8;
438     p_response[8] = i_session_id & 0xff;
439
440     if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) !=
441             VLC_SUCCESS )
442     {
443         msg_Err( p_access,
444                  "SessionOpen: couldn't send TPDU on slot %d", i_slot );
445         return;
446     }
447     if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
448     {
449         msg_Err( p_access,
450                  "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
451         return;
452     }
453
454     switch ( i_resource_id )
455     {
456     case RI_RESOURCE_MANAGER:
457         ResourceManagerOpen( p_access, i_session_id ); break; 
458     case RI_APPLICATION_INFORMATION:
459         ApplicationInformationOpen( p_access, i_session_id ); break; 
460     case RI_CONDITIONAL_ACCESS_SUPPORT:
461         ConditionalAccessOpen( p_access, i_session_id ); break; 
462     case RI_DATE_TIME:
463         DateTimeOpen( p_access, i_session_id ); break; 
464     case RI_MMI:
465         MMIOpen( p_access, i_session_id ); break; 
466
467     case RI_HOST_CONTROL:
468     default:
469         msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
470         p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
471     }
472 }
473
474 /*****************************************************************************
475  * SessionSendClose
476  *****************************************************************************/
477 static void SessionSendClose( access_t * p_access, int i_session_id )
478 {
479     access_sys_t *p_sys = p_access->p_sys;
480     uint8_t p_response[16];
481     uint8_t i_tag;
482     uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
483
484     p_response[0] = ST_CLOSE_SESSION_REQUEST;
485     p_response[1] = 0x2;
486     p_response[2] = i_session_id >> 8;
487     p_response[3] = i_session_id & 0xff;
488
489     if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
490             VLC_SUCCESS )
491     {
492         msg_Err( p_access,
493                  "SessionSendClose: couldn't send TPDU on slot %d", i_slot );
494         return;
495     }
496     if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
497     {
498         msg_Err( p_access,
499                  "SessionSendClose: couldn't recv TPDU on slot %d", i_slot );
500         return;
501     }
502 }
503
504 /*****************************************************************************
505  * SessionClose
506  *****************************************************************************/
507 static void SessionClose( access_t * p_access, int i_session_id )
508 {
509     access_sys_t *p_sys = p_access->p_sys;
510     uint8_t p_response[16];
511     uint8_t i_tag;
512     uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
513
514     if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
515         p_sys->p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
516     p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
517
518     p_response[0] = ST_CLOSE_SESSION_RESPONSE;
519     p_response[1] = 0x3;
520     p_response[2] = SS_OK;
521     p_response[3] = i_session_id >> 8;
522     p_response[4] = i_session_id & 0xff;
523
524     if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
525             VLC_SUCCESS )
526     {
527         msg_Err( p_access,
528                  "SessionClose: couldn't send TPDU on slot %d", i_slot );
529         return;
530     }
531     if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
532     {
533         msg_Err( p_access,
534                  "SessionClose: couldn't recv TPDU on slot %d", i_slot );
535         return;
536     }
537 }
538
539 /*****************************************************************************
540  * SPDUHandle
541  *****************************************************************************/
542 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
543                         uint8_t *p_spdu, int i_size )
544 {
545     access_sys_t *p_sys = p_access->p_sys;
546     int i_session_id;
547
548     switch ( p_spdu[0] )
549     {
550     case ST_SESSION_NUMBER:
551         if ( i_size <= 4 )
552             return;
553         i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
554         p_sys->p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
555                                                        p_spdu + 4, i_size - 4 );
556         break;
557
558     case ST_OPEN_SESSION_REQUEST:
559         if ( i_size != 6 || p_spdu[1] != 0x4 )
560             return;
561         SessionOpen( p_access, i_slot, p_spdu, i_size );
562         break;
563
564     case ST_CLOSE_SESSION_REQUEST:
565         i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
566         SessionClose( p_access, i_session_id );
567         break;
568
569     case ST_CLOSE_SESSION_RESPONSE:
570         i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
571         if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
572             p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
573                                                           i_session_id );
574         p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
575         break;
576
577     default:
578         break;
579     }
580 }
581
582
583 /*
584  * Application layer
585  */
586
587 #define AOT_NONE                    0x000000
588 #define AOT_PROFILE_ENQ             0x9F8010
589 #define AOT_PROFILE                 0x9F8011
590 #define AOT_PROFILE_CHANGE          0x9F8012
591 #define AOT_APPLICATION_INFO_ENQ    0x9F8020
592 #define AOT_APPLICATION_INFO        0x9F8021
593 #define AOT_ENTER_MENU              0x9F8022
594 #define AOT_CA_INFO_ENQ             0x9F8030
595 #define AOT_CA_INFO                 0x9F8031
596 #define AOT_CA_PMT                  0x9F8032
597 #define AOT_CA_PMT_REPLY            0x9F8033
598 #define AOT_TUNE                    0x9F8400
599 #define AOT_REPLACE                 0x9F8401
600 #define AOT_CLEAR_REPLACE           0x9F8402
601 #define AOT_ASK_RELEASE             0x9F8403
602 #define AOT_DATE_TIME_ENQ           0x9F8440
603 #define AOT_DATE_TIME               0x9F8441
604 #define AOT_CLOSE_MMI               0x9F8800
605 #define AOT_DISPLAY_CONTROL         0x9F8801
606 #define AOT_DISPLAY_REPLY           0x9F8802
607 #define AOT_TEXT_LAST               0x9F8803
608 #define AOT_TEXT_MORE               0x9F8804
609 #define AOT_KEYPAD_CONTROL          0x9F8805
610 #define AOT_KEYPRESS                0x9F8806
611 #define AOT_ENQ                     0x9F8807
612 #define AOT_ANSW                    0x9F8808
613 #define AOT_MENU_LAST               0x9F8809
614 #define AOT_MENU_MORE               0x9F880A
615 #define AOT_MENU_ANSW               0x9F880B
616 #define AOT_LIST_LAST               0x9F880C
617 #define AOT_LIST_MORE               0x9F880D
618 #define AOT_SUBTITLE_SEGMENT_LAST   0x9F880E
619 #define AOT_SUBTITLE_SEGMENT_MORE   0x9F880F
620 #define AOT_DISPLAY_MESSAGE         0x9F8810
621 #define AOT_SCENE_END_MARK          0x9F8811
622 #define AOT_SCENE_DONE              0x9F8812
623 #define AOT_SCENE_CONTROL           0x9F8813
624 #define AOT_SUBTITLE_DOWNLOAD_LAST  0x9F8814
625 #define AOT_SUBTITLE_DOWNLOAD_MORE  0x9F8815
626 #define AOT_FLUSH_DOWNLOAD          0x9F8816
627 #define AOT_DOWNLOAD_REPLY          0x9F8817
628 #define AOT_COMMS_CMD               0x9F8C00
629 #define AOT_CONNECTION_DESCRIPTOR   0x9F8C01
630 #define AOT_COMMS_REPLY             0x9F8C02
631 #define AOT_COMMS_SEND_LAST         0x9F8C03
632 #define AOT_COMMS_SEND_MORE         0x9F8C04
633 #define AOT_COMMS_RCV_LAST          0x9F8C05
634 #define AOT_COMMS_RCV_MORE          0x9F8C06
635
636 /*****************************************************************************
637  * APDUGetTag
638  *****************************************************************************/
639 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
640 {
641     if ( i_size >= 3 )
642     {
643         int i, t = 0;
644         for ( i = 0; i < 3; i++ )
645             t = (t << 8) | *p_apdu++;
646         return t;
647     }
648
649     return AOT_NONE;
650 }
651
652 /*****************************************************************************
653  * APDUGetLength
654  *****************************************************************************/
655 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
656 {
657     return GetLength( &p_apdu[3], pi_size );
658 }
659
660 /*****************************************************************************
661  * APDUSend
662  *****************************************************************************/
663 static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
664                      uint8_t *p_data, int i_size )
665 {
666     access_sys_t *p_sys = p_access->p_sys;
667     uint8_t *p_apdu = malloc( i_size + 12 );
668     uint8_t *p = p_apdu;
669     ca_msg_t ca_msg;
670     int i_ret;
671
672     *p++ = (i_tag >> 16);
673     *p++ = (i_tag >> 8) & 0xff;
674     *p++ = i_tag & 0xff;
675     p = SetLength( p, i_size );
676     if ( i_size )
677         memcpy( p, p_data, i_size );
678     if( p_sys->i_ca_type == CA_CI_LINK )
679     {
680         i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
681     }
682     else
683     {
684         if( i_size + p - p_apdu >256 )
685         {
686             msg_Err( p_access, "CAM: apdu overflow" );
687             i_ret = VLC_EGENERIC;
688         }
689         else
690         {
691             char *psz_hex;
692             ca_msg.length = i_size + p - p_apdu;
693             if( i_size == 0 ) ca_msg.length=3;
694             psz_hex = (char*)malloc( ca_msg.length*3 + 1);
695             memcpy( ca_msg.msg, p_apdu, i_size + p - p_apdu );
696             i_ret = ioctl(p_sys->i_ca_handle, CA_SEND_MSG, &ca_msg );
697             if( i_ret < 0 )
698             {
699                 msg_Err( p_access, "Error sending to CAM: %s", strerror(errno) );
700                 i_ret = VLC_EGENERIC;
701             }
702         }
703     }
704     free( p_apdu );
705     return i_ret;
706 }
707
708 /*
709  * Resource Manager
710  */
711
712 /*****************************************************************************
713  * ResourceManagerHandle
714  *****************************************************************************/
715 static void ResourceManagerHandle( access_t * p_access, int i_session_id,
716                                    uint8_t *p_apdu, int i_size )
717 {
718     int i_tag = APDUGetTag( p_apdu, i_size );
719
720     switch ( i_tag )
721     {
722     case AOT_PROFILE_ENQ:
723     {
724         int resources[] = { htonl(RI_RESOURCE_MANAGER),
725                             htonl(RI_APPLICATION_INFORMATION),
726                             htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
727                             htonl(RI_DATE_TIME),
728                             htonl(RI_MMI)
729                           };
730         APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
731                   sizeof(resources) );
732         break;
733     }
734     case AOT_PROFILE:
735         APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
736         break;
737
738     default:
739         msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
740                  i_tag );
741     }
742 }
743
744 /*****************************************************************************
745  * ResourceManagerOpen
746  *****************************************************************************/
747 static void ResourceManagerOpen( access_t * p_access, int i_session_id )
748 {
749     access_sys_t *p_sys = p_access->p_sys;
750
751     msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
752
753     p_sys->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
754
755     APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
756 }
757
758 /*
759  * Application Information
760  */
761
762 /*****************************************************************************
763  * ApplicationInformationHandle
764  *****************************************************************************/
765 static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
766                                           uint8_t *p_apdu, int i_size )
767 {
768     int i_tag = APDUGetTag( p_apdu, i_size );
769
770     switch ( i_tag )
771     {
772     case AOT_APPLICATION_INFO:
773     {
774         int i_type, i_manufacturer, i_code;
775         int l = 0;
776         uint8_t *d = APDUGetLength( p_apdu, &l );
777
778         if ( l < 4 ) break;
779         p_apdu[l + 4] = '\0';
780
781         i_type = *d++;
782         i_manufacturer = ((int)d[0] << 8) | d[1];
783         d += 2;
784         i_code = ((int)d[0] << 8) | d[1];
785         d += 2;
786         d = GetLength( d, &l );
787         d[l] = '\0';
788         msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
789                   d, i_type, i_manufacturer, i_code );
790         break;
791     }
792     default:
793         msg_Err( p_access,
794                  "unexpected tag in ApplicationInformationHandle (0x%x)",
795                  i_tag );
796     }
797 }
798
799 /*****************************************************************************
800  * ApplicationInformationOpen
801  *****************************************************************************/
802 static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
803 {
804     access_sys_t *p_sys = p_access->p_sys;
805
806     msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
807
808     p_sys->p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
809
810     APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
811 }
812
813 /*
814  * Conditional Access
815  */
816
817 #define MAX_CASYSTEM_IDS 16
818
819 typedef struct
820 {
821     uint16_t pi_system_ids[MAX_CASYSTEM_IDS + 1];
822 } system_ids_t;
823
824 static vlc_bool_t CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
825 {
826     int i = 0;
827     if( !p_ids ) return VLC_TRUE;
828
829     while ( p_ids->pi_system_ids[i] )
830     {
831         if ( p_ids->pi_system_ids[i] == i_id )
832             return VLC_TRUE;
833         i++;
834     }
835
836     return VLC_FALSE;
837 }
838
839 /*****************************************************************************
840  * CAPMTNeedsDescrambling
841  *****************************************************************************/
842 static vlc_bool_t CAPMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
843 {
844     dvbpsi_descriptor_t *p_dr;
845     dvbpsi_pmt_es_t *p_es;
846
847     for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
848     {
849         if( p_dr->i_tag == 0x9 )
850         {
851             return VLC_TRUE;
852         }
853     }
854     
855     for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
856     {
857         for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
858              p_dr = p_dr->p_next )
859         {
860             if( p_dr->i_tag == 0x9 )
861             {
862                 return VLC_TRUE;
863             }
864         }
865     }
866
867     return VLC_FALSE;
868 }
869
870 /*****************************************************************************
871  * CAPMTBuild
872  *****************************************************************************/
873 static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr )
874 {
875     int i_cad_size = 0;
876
877     while ( p_dr != NULL )
878     {
879         if( p_dr->i_tag == 0x9 )
880         {
881             uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
882                                     | p_dr->p_data[1];
883             if ( CheckSystemID( p_ids, i_sysid ) )
884                 i_cad_size += p_dr->i_length + 2;
885         }
886         p_dr = p_dr->p_next;
887     }
888
889     return i_cad_size;
890 }
891
892 static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt,
893                              uint16_t i_program_number, uint8_t i_version,
894                              int i_size, dvbpsi_descriptor_t *p_dr,
895                              uint8_t i_cmd )
896 {
897     uint8_t *p_data;
898
899     if ( i_size )
900         p_data = malloc( 7 + i_size );
901     else
902         p_data = malloc( 6 );
903
904     p_data[0] = i_list_mgt;
905     p_data[1] = i_program_number >> 8;
906     p_data[2] = i_program_number & 0xff;
907     p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
908
909     if ( i_size )
910     {
911         int i;
912
913         p_data[4] = (i_size + 1) >> 8;
914         p_data[5] = (i_size + 1) & 0xff;
915         p_data[6] = i_cmd;
916         i = 7;
917
918         while ( p_dr != NULL )
919         {
920             if( p_dr->i_tag == 0x9 )
921             {
922                 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
923                                     | p_dr->p_data[1];
924                 if ( CheckSystemID( p_ids, i_sysid ) )
925                 {
926                     p_data[i] = 0x9;
927                     p_data[i + 1] = p_dr->i_length;
928                     memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
929 //                    p_data[i+4] &= 0x1f;
930                     i += p_dr->i_length + 2;
931                 }
932             }
933             p_dr = p_dr->p_next;
934         }
935     }
936     else
937     {
938         p_data[4] = 0;
939         p_data[5] = 0;
940     }
941
942     return p_data;
943 }
944
945 static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt,
946                          int i_capmt_size, uint8_t i_type, uint16_t i_pid,
947                          int i_size, dvbpsi_descriptor_t *p_dr,
948                          uint8_t i_cmd )
949 {
950     uint8_t *p_data;
951     int i;
952     
953     if ( i_size )
954         p_data = realloc( p_capmt, i_capmt_size + 6 + i_size );
955     else
956         p_data = realloc( p_capmt, i_capmt_size + 5 );
957
958     i = i_capmt_size;
959
960     p_data[i] = i_type;
961     p_data[i + 1] = i_pid >> 8;
962     p_data[i + 2] = i_pid & 0xff;
963
964     if ( i_size )
965     {
966         p_data[i + 3] = (i_size + 1) >> 8;
967         p_data[i + 4] = (i_size + 1) & 0xff;
968         p_data[i + 5] = i_cmd;
969         i += 6;
970
971         while ( p_dr != NULL )
972         {
973             if( p_dr->i_tag == 0x9 )
974             {
975                 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
976                                     | p_dr->p_data[1];
977                 if ( CheckSystemID( p_ids, i_sysid ) )
978                 {
979                     p_data[i] = 0x9;
980                     p_data[i + 1] = p_dr->i_length;
981                     memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
982                     i += p_dr->i_length + 2;
983                 }
984             }
985             p_dr = p_dr->p_next;
986         }
987     }
988     else
989     {
990         p_data[i + 3] = 0;
991         p_data[i + 4] = 0;
992     }
993
994     return p_data;
995 }
996
997 static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id,
998                             dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt,
999                             uint8_t i_cmd, int *pi_capmt_size )
1000 {
1001     access_sys_t *p_sys = p_access->p_sys;
1002     system_ids_t *p_ids =
1003         (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1004     dvbpsi_pmt_es_t *p_es;
1005     int i_cad_size, i_cad_program_size;
1006     uint8_t *p_capmt;
1007
1008     i_cad_size = i_cad_program_size =
1009             GetCADSize( p_ids, p_pmt->p_first_descriptor );
1010     for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1011     {
1012         i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor );
1013     }
1014
1015     if ( !i_cad_size )
1016     {
1017         msg_Warn( p_access,
1018                   "no compatible scrambling system for SID %d on session %d",
1019                   p_pmt->i_program_number, i_session_id );
1020         *pi_capmt_size = 0;
1021         return NULL;
1022     }
1023
1024     p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number,
1025                            p_pmt->i_version, i_cad_program_size,
1026                            p_pmt->p_first_descriptor, i_cmd );
1027
1028     if ( i_cad_program_size )
1029         *pi_capmt_size = 7 + i_cad_program_size;
1030     else
1031         *pi_capmt_size = 6;
1032
1033     for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1034     {
1035         i_cad_size = GetCADSize( p_ids, p_es->p_first_descriptor );
1036
1037         if ( i_cad_size || i_cad_program_size )
1038         {
1039             p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type,
1040                                p_es->i_pid, i_cad_size,
1041                                p_es->p_first_descriptor, i_cmd );
1042             if ( i_cad_size )
1043                 *pi_capmt_size += 6 + i_cad_size;
1044             else
1045                 *pi_capmt_size += 5;
1046         }
1047     }
1048
1049     return p_capmt;
1050 }
1051
1052 /*****************************************************************************
1053  * CAPMTFirst
1054  *****************************************************************************/
1055 static void CAPMTFirst( access_t * p_access, int i_session_id,
1056                         dvbpsi_pmt_t *p_pmt )
1057 {
1058     uint8_t *p_capmt;
1059     int i_capmt_size;
1060
1061     msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d",
1062              p_pmt->i_program_number, i_session_id );
1063
1064     p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1065                           0x3 /* only */, 0x1 /* ok_descrambling */,
1066                           &i_capmt_size );
1067
1068     if ( i_capmt_size )
1069         APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1070 }
1071
1072 /*****************************************************************************
1073  * CAPMTAdd
1074  *****************************************************************************/
1075 static void CAPMTAdd( access_t * p_access, int i_session_id,
1076                       dvbpsi_pmt_t *p_pmt )
1077 {
1078     uint8_t *p_capmt;
1079     int i_capmt_size;
1080
1081     if( p_access->p_sys->i_selected_programs >= CAM_PROG_MAX )
1082     {
1083         msg_Warn( p_access, "Not adding CAPMT for SID %d, too many programs",
1084                   p_pmt->i_program_number );
1085         return;
1086     }
1087     p_access->p_sys->i_selected_programs++;
1088     if( p_access->p_sys->i_selected_programs == 1 )
1089     {
1090         CAPMTFirst( p_access, i_session_id, p_pmt );
1091         return;
1092     }
1093         
1094     
1095     msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
1096              p_pmt->i_program_number, i_session_id );
1097
1098     p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1099                           0x4 /* add */, 0x1 /* ok_descrambling */,
1100                           &i_capmt_size );
1101
1102     if ( i_capmt_size )
1103         APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1104 }
1105
1106 /*****************************************************************************
1107  * CAPMTUpdate
1108  *****************************************************************************/
1109 static void CAPMTUpdate( access_t * p_access, int i_session_id,
1110                          dvbpsi_pmt_t *p_pmt )
1111 {
1112     uint8_t *p_capmt;
1113     int i_capmt_size;
1114
1115     msg_Dbg( p_access, "updating CAPMT for SID %d on session %d",
1116              p_pmt->i_program_number, i_session_id );
1117
1118     p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1119                           0x5 /* update */, 0x1 /* ok_descrambling */,
1120                           &i_capmt_size );
1121
1122     if ( i_capmt_size )
1123         APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1124 }
1125
1126 /*****************************************************************************
1127  * CAPMTDelete
1128  *****************************************************************************/
1129 static void CAPMTDelete( access_t * p_access, int i_session_id,
1130                          dvbpsi_pmt_t *p_pmt )
1131 {
1132     uint8_t *p_capmt;
1133     int i_capmt_size;
1134
1135     p_access->p_sys->i_selected_programs--;
1136     msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d",
1137              p_pmt->i_program_number, i_session_id );
1138
1139     p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1140                           0x5 /* update */, 0x4 /* not selected */,
1141                           &i_capmt_size );
1142
1143     if ( i_capmt_size )
1144         APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1145 }
1146
1147 /*****************************************************************************
1148  * ConditionalAccessHandle
1149  *****************************************************************************/
1150 static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
1151                                      uint8_t *p_apdu, int i_size )
1152 {
1153     access_sys_t *p_sys = p_access->p_sys;
1154     system_ids_t *p_ids =
1155         (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1156     int i_tag = APDUGetTag( p_apdu, i_size );
1157
1158     switch ( i_tag )
1159     {
1160     case AOT_CA_INFO:
1161     {
1162         int i;
1163         int l = 0;
1164         uint8_t *d = APDUGetLength( p_apdu, &l );
1165         msg_Dbg( p_access, "CA system IDs supported by the application :" );
1166
1167         for ( i = 0; i < l / 2; i++ )
1168         {
1169             p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
1170             d += 2;
1171             msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] );
1172         }
1173         p_ids->pi_system_ids[i] = 0;
1174
1175         for ( i = 0; i < MAX_PROGRAMS; i++ )
1176         {
1177             if ( p_sys->pp_selected_programs[i] != NULL )
1178             {
1179                 CAPMTAdd( p_access, i_session_id,
1180                           p_sys->pp_selected_programs[i] );
1181             }
1182         }
1183         break;
1184     }
1185
1186     default:
1187         msg_Err( p_access,
1188                  "unexpected tag in ConditionalAccessHandle (0x%x)",
1189                  i_tag );
1190     }
1191 }
1192
1193 /*****************************************************************************
1194  * ConditionalAccessOpen
1195  *****************************************************************************/
1196 static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
1197 {
1198     access_sys_t *p_sys = p_access->p_sys;
1199
1200     msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
1201
1202     p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
1203     p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(system_ids_t));
1204     memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0,
1205             sizeof(system_ids_t) );
1206
1207     APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
1208 }
1209
1210 /*
1211  * Date Time
1212  */
1213
1214 typedef struct
1215 {
1216     int i_interval;
1217     mtime_t i_last;
1218 } date_time_t;
1219
1220 /*****************************************************************************
1221  * DateTimeSend
1222  *****************************************************************************/
1223 static void DateTimeSend( access_t * p_access, int i_session_id )
1224 {
1225     access_sys_t *p_sys = p_access->p_sys;
1226     date_time_t *p_date =
1227         (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1228
1229     time_t t = time(NULL);
1230     struct tm tm_gmt;
1231     struct tm tm_loc;
1232
1233     if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
1234     {
1235         int Y = tm_gmt.tm_year;
1236         int M = tm_gmt.tm_mon + 1;
1237         int D = tm_gmt.tm_mday;
1238         int L = (M == 1 || M == 2) ? 1 : 0;
1239         int MJD = 14956 + D + (int)((Y - L) * 365.25)
1240                     + (int)((M + 1 + L * 12) * 30.6001);
1241         uint8_t p_response[7];
1242
1243 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
1244
1245         p_response[0] = htons(MJD) >> 8;
1246         p_response[1] = htons(MJD) & 0xff;
1247         p_response[2] = DEC2BCD(tm_gmt.tm_hour);
1248         p_response[3] = DEC2BCD(tm_gmt.tm_min);
1249         p_response[4] = DEC2BCD(tm_gmt.tm_sec);
1250         p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
1251         p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
1252
1253         APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
1254
1255         p_date->i_last = mdate();
1256     }
1257 }
1258
1259 /*****************************************************************************
1260  * DateTimeHandle
1261  *****************************************************************************/
1262 static void DateTimeHandle( access_t * p_access, int i_session_id,
1263                             uint8_t *p_apdu, int i_size )
1264 {
1265     access_sys_t *p_sys = p_access->p_sys;
1266     date_time_t *p_date =
1267         (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1268
1269     int i_tag = APDUGetTag( p_apdu, i_size );
1270
1271     switch ( i_tag )
1272     {
1273     case AOT_DATE_TIME_ENQ:
1274     {
1275         int l;
1276         const uint8_t *d = APDUGetLength( p_apdu, &l );
1277
1278         if ( l > 0 )
1279         {
1280             p_date->i_interval = *d;
1281             msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
1282                      p_date->i_interval );
1283         }
1284         else
1285             p_date->i_interval = 0;
1286
1287         DateTimeSend( p_access, i_session_id );
1288         break;
1289     }
1290     default:
1291         msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
1292     }
1293 }
1294
1295 /*****************************************************************************
1296  * DateTimeManage
1297  *****************************************************************************/
1298 static void DateTimeManage( access_t * p_access, int i_session_id )
1299 {
1300     access_sys_t *p_sys = p_access->p_sys;
1301     date_time_t *p_date =
1302         (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1303
1304     if ( p_date->i_interval
1305           && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
1306     {
1307         DateTimeSend( p_access, i_session_id );
1308     }
1309 }
1310
1311 /*****************************************************************************
1312  * DateTimeOpen
1313  *****************************************************************************/
1314 static void DateTimeOpen( access_t * p_access, int i_session_id )
1315 {
1316     access_sys_t *p_sys = p_access->p_sys;
1317
1318     msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
1319
1320     p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
1321     p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
1322     p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t));
1323     memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
1324
1325     DateTimeSend( p_access, i_session_id );
1326 }
1327
1328 /*
1329  * MMI
1330  */
1331
1332 /* Display Control Commands */
1333
1334 #define DCC_SET_MMI_MODE                          0x01
1335 #define DCC_DISPLAY_CHARACTER_TABLE_LIST          0x02
1336 #define DCC_INPUT_CHARACTER_TABLE_LIST            0x03
1337 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS      0x04
1338 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS  0x05
1339
1340 /* MMI Modes */
1341
1342 #define MM_HIGH_LEVEL                      0x01
1343 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS      0x02
1344 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS  0x03
1345
1346 /* Display Reply IDs */
1347
1348 #define DRI_MMI_MODE_ACK                              0x01
1349 #define DRI_LIST_DISPLAY_CHARACTER_TABLES             0x02
1350 #define DRI_LIST_INPUT_CHARACTER_TABLES               0x03
1351 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS      0x04
1352 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS  0x05
1353 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD               0xF0
1354 #define DRI_UNKNOWN_MMI_MODE                          0xF1
1355 #define DRI_UNKNOWN_CHARACTER_TABLE                   0xF2
1356
1357 /* Enquiry Flags */
1358
1359 #define EF_BLIND  0x01
1360
1361 /* Answer IDs */
1362
1363 #define AI_CANCEL  0x00
1364 #define AI_ANSWER  0x01
1365
1366 /*****************************************************************************
1367  * MMIDisplayReply
1368  *****************************************************************************/
1369 static void MMIDisplayReply( access_t *p_access, int i_session_id )
1370 {
1371     uint8_t p_response[2];
1372
1373     p_response[0] = DRI_MMI_MODE_ACK;
1374     p_response[1] = MM_HIGH_LEVEL;
1375
1376     APDUSend( p_access, i_session_id, AOT_DISPLAY_REPLY, p_response, 2 );
1377
1378     msg_Dbg( p_access, "sending DisplayReply on session (%d)", i_session_id );
1379 }
1380
1381 /*****************************************************************************
1382  * MMIGetText
1383  *****************************************************************************/
1384 static char *MMIGetText( access_t *p_access, char *psz_text,
1385                          uint8_t **pp_apdu, int *pi_size )
1386 {
1387     int i_tag = APDUGetTag( *pp_apdu, *pi_size );
1388     int l;
1389     uint8_t *d;
1390
1391     if ( i_tag != AOT_TEXT_LAST )
1392     {
1393         msg_Err( p_access, "unexpected text tag: %06x", i_tag );
1394         psz_text[0] = '\0';
1395         *pi_size = 0;
1396         return psz_text;
1397     }
1398
1399     d = APDUGetLength( *pp_apdu, &l );
1400     strncpy( psz_text, (char *)d, l );
1401     psz_text[l] = '\0';
1402
1403     *pp_apdu += l + 4;
1404     *pi_size -= l + 4;
1405     return psz_text;
1406 }
1407
1408 /*****************************************************************************
1409  * MMIHandle
1410  *****************************************************************************/
1411 static void MMIHandle( access_t *p_access, int i_session_id,
1412                        uint8_t *p_apdu, int i_size )
1413 {
1414     int i_tag = APDUGetTag( p_apdu, i_size );
1415
1416     switch ( i_tag )
1417     {
1418     case AOT_DISPLAY_CONTROL:
1419     {
1420         int l;
1421         uint8_t *d = APDUGetLength( p_apdu, &l );
1422
1423         if ( l > 0 )
1424         {
1425             switch ( *d )
1426             {
1427             case DCC_SET_MMI_MODE:
1428                 if ( l == 2 && d[1] == MM_HIGH_LEVEL )
1429                     MMIDisplayReply( p_access, i_session_id );
1430                 else
1431                     msg_Err( p_access, "unsupported MMI mode %02x", d[1] );
1432                 break;
1433
1434             default:
1435                 msg_Err( p_access, "unsupported display control command %02x",
1436                          *d );
1437                 break;
1438             }
1439         }
1440         break;
1441     }
1442
1443     case AOT_LIST_LAST:
1444     case AOT_MENU_LAST:
1445     {
1446         int l;
1447         uint8_t *d = APDUGetLength( p_apdu, &l );
1448         char psz_text[255];
1449
1450         if ( l > 0 )
1451         {
1452             l--; d++; /* choice_nb */
1453
1454             if ( l > 0 )
1455                 msg_Info( p_access, "MMI title: %s",
1456                           MMIGetText( p_access, psz_text, &d, &l ) );
1457             if ( l > 0 )
1458                 msg_Info( p_access, "MMI subtitle: %s",
1459                           MMIGetText( p_access, psz_text, &d, &l ) );
1460             if ( l > 0 )
1461                 msg_Info( p_access, "MMI bottom: %s",
1462                           MMIGetText( p_access, psz_text, &d, &l ) );
1463             while ( l > 0 )
1464             {
1465                 msg_Info( p_access, "MMI: %s",
1466                           MMIGetText( p_access, psz_text, &d, &l ) );
1467             }
1468         }
1469         break;
1470     }
1471
1472     case AOT_CLOSE_MMI:
1473         SessionSendClose( p_access, i_session_id );
1474         msg_Dbg( p_access, "closing MMI session (%d)", i_session_id );
1475         break;
1476
1477     default:
1478         msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
1479     }
1480 }
1481
1482 /*****************************************************************************
1483  * MMIOpen
1484  *****************************************************************************/
1485 static void MMIOpen( access_t *p_access, int i_session_id )
1486 {
1487     access_sys_t *p_sys = p_access->p_sys;
1488
1489     msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
1490
1491     p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
1492 }
1493
1494
1495 /*
1496  * Hardware handling
1497  */
1498
1499 /*****************************************************************************
1500  * InitSlot: Open the transport layer
1501  *****************************************************************************/
1502 #define MAX_TC_RETRIES 20
1503
1504 static int InitSlot( access_t * p_access, int i_slot )
1505 {
1506     access_sys_t *p_sys = p_access->p_sys;
1507     int i;
1508
1509     if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1510             != VLC_SUCCESS )
1511     {
1512         msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
1513                  i_slot );
1514         return VLC_EGENERIC;
1515     }
1516
1517     /* This is out of the spec */
1518     for ( i = 0; i < MAX_TC_RETRIES; i++ )
1519     {
1520         uint8_t i_tag;
1521         if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
1522               && i_tag == T_CTC_REPLY )
1523         {
1524             p_sys->pb_active_slot[i_slot] = VLC_TRUE;
1525             break;
1526         }
1527
1528         if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1529                 != VLC_SUCCESS )
1530         {
1531             msg_Err( p_access,
1532                      "en50221_Init: couldn't send TPDU on slot %d",
1533                      i_slot );
1534             continue;
1535         }
1536     }
1537     if ( p_sys->pb_active_slot[i_slot] )
1538     {
1539         p_sys->i_ca_timeout = 100000;
1540         return VLC_SUCCESS;
1541     }
1542
1543     return VLC_EGENERIC;
1544 }
1545
1546
1547 /*
1548  * External entry points
1549  */
1550 /*****************************************************************************
1551  * en50221_Init : Initialize the CAM for en50221
1552  *****************************************************************************/
1553 int E_(en50221_Init)( access_t * p_access )
1554 {
1555     access_sys_t *p_sys = p_access->p_sys;
1556
1557     if( p_sys->i_ca_type & CA_CI_LINK )
1558     {
1559         int i_slot;
1560         for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1561         {
1562             if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
1563             {
1564                 msg_Err( p_access, "en50221_Init: couldn't reset slot %d",
1565                          i_slot );
1566             }
1567         }
1568
1569         p_sys->i_ca_timeout = 100000;
1570         /* Wait a bit otherwise it doesn't initialize properly... */
1571         msleep( 1000000 );
1572
1573         return VLC_SUCCESS;
1574     }
1575     else
1576     {
1577         struct ca_slot_info info;
1578
1579         /* We don't reset the CAM in that case because it's done by the
1580          * ASIC. */
1581         if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &info ) < 0 )
1582         {
1583             msg_Err( p_access, "en50221_Init: couldn't get slot info" );
1584             close( p_sys->i_ca_handle );
1585             p_sys->i_ca_handle = 0;
1586             return VLC_EGENERIC;
1587         }
1588         if( info.flags == 0 )
1589         {
1590             msg_Err( p_access, "en50221_Init: no CAM inserted" );
1591             close( p_sys->i_ca_handle );
1592             p_sys->i_ca_handle = 0;
1593             return VLC_EGENERIC;
1594         }
1595
1596         /* Allocate a dummy sessions */
1597         p_sys->p_sessions[ 0 ].i_resource_id = RI_CONDITIONAL_ACCESS_SUPPORT;
1598
1599         /* Get application info to find out which cam we are using and make
1600            sure everything is ready to play */
1601         ca_msg_t ca_msg;
1602         ca_msg.length=3;
1603         ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1604         ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1605         ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1606         memset( &ca_msg.msg[3], 0, 253 );
1607         APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1608         if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1609         {
1610             msg_Err( p_access, "en50221_Init: failed getting message" );
1611             return VLC_EGENERIC;
1612         }
1613
1614 #if HLCI_WAIT_CAM_READY
1615         while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1616         {
1617             if( p_access->b_die ) return VLC_EGENERIC;
1618             msleep(1);
1619             msg_Dbg( p_access, "CAM: please wait" );
1620             APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1621             ca_msg.length=3;
1622             ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1623             ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1624             ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1625             memset( &ca_msg.msg[3], 0, 253 );
1626             if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1627             {
1628                 msg_Err( p_access, "en50221_Init: failed getting message" );
1629                 return VLC_EGENERIC;
1630             }
1631             msg_Dbg( p_access, "en50221_Init: Got length: %d, tag: 0x%x", ca_msg.length, APDUGetTag( ca_msg.msg, ca_msg.length ) );
1632         }
1633 #else
1634         if( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1635         {
1636             msg_Err( p_access, "CAM returns garbage as application info!" );
1637             return VLC_EGENERIC;
1638         }
1639 #endif
1640         msg_Dbg( p_access, "found CAM %s using id 0x%x", &ca_msg.msg[12],
1641                  (ca_msg.msg[8]<<8)|ca_msg.msg[9] );
1642         return VLC_SUCCESS;
1643     }
1644 }
1645
1646 /*****************************************************************************
1647  * en50221_Poll : Poll the CAM for TPDUs
1648  *****************************************************************************/
1649 int E_(en50221_Poll)( access_t * p_access )
1650 {
1651     access_sys_t *p_sys = p_access->p_sys;
1652     int i_slot;
1653     int i_session_id;
1654
1655     for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1656     {
1657         uint8_t i_tag;
1658
1659         if ( !p_sys->pb_active_slot[i_slot] )
1660         {
1661             ca_slot_info_t sinfo;
1662             sinfo.num = i_slot;
1663             if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
1664             {
1665                 msg_Err( p_access, "en50221_Poll: couldn't get info on slot %d",
1666                          i_slot );
1667                 continue;
1668             }
1669
1670             if ( sinfo.flags & CA_CI_MODULE_READY )
1671             {
1672                 msg_Dbg( p_access, "en50221_Poll: slot %d is active",
1673                          i_slot );
1674                 p_sys->pb_active_slot[i_slot] = VLC_TRUE;
1675             }
1676             else
1677                 continue;
1678
1679             InitSlot( p_access, i_slot );
1680         }
1681
1682         if ( !p_sys->pb_tc_has_data[i_slot] )
1683         {
1684             if ( TPDUSend( p_access, i_slot, T_DATA_LAST, NULL, 0 ) !=
1685                     VLC_SUCCESS )
1686             {
1687                 msg_Err( p_access,
1688                          "en50221_Poll: couldn't send TPDU on slot %d",
1689                          i_slot );
1690                 continue;
1691             }
1692             if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) !=
1693                     VLC_SUCCESS )
1694             {
1695                 msg_Err( p_access,
1696                          "en50221_Poll: couldn't recv TPDU on slot %d",
1697                          i_slot );
1698                 continue;
1699             }
1700         }
1701
1702         while ( p_sys->pb_tc_has_data[i_slot] )
1703         {
1704             uint8_t p_tpdu[MAX_TPDU_SIZE];
1705             int i_size, i_session_size;
1706             uint8_t *p_session;
1707
1708             if ( TPDUSend( p_access, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
1709             {
1710                 msg_Err( p_access,
1711                          "en50221_Poll: couldn't send TPDU on slot %d",
1712                          i_slot );
1713                 continue;
1714             }
1715             if ( TPDURecv( p_access, i_slot, &i_tag, p_tpdu, &i_size ) !=
1716                     VLC_SUCCESS )
1717             {
1718                 msg_Err( p_access,
1719                          "en50221_Poll: couldn't recv TPDU on slot %d",
1720                          i_slot );
1721                 continue;
1722             }
1723
1724             p_session = GetLength( &p_tpdu[3], &i_session_size );
1725             if ( i_session_size <= 1 )
1726                 continue;
1727
1728             p_session++;
1729             i_session_size--;
1730
1731             if ( i_tag != T_DATA_LAST )
1732             {
1733                 msg_Err( p_access,
1734                          "en50221_Poll: fragmented TPDU not supported" );
1735                 break;
1736             }
1737
1738             SPDUHandle( p_access, i_slot, p_session, i_session_size );
1739         }
1740     }
1741
1742     for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
1743     {
1744         if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1745               && p_sys->p_sessions[i_session_id - 1].pf_manage )
1746         {
1747             p_sys->p_sessions[i_session_id - 1].pf_manage( p_access,
1748                                                            i_session_id );
1749         }
1750     }
1751
1752     return VLC_SUCCESS;
1753 }
1754
1755
1756 /*****************************************************************************
1757  * en50221_SetCAPMT :
1758  *****************************************************************************/
1759 int E_(en50221_SetCAPMT)( access_t * p_access, dvbpsi_pmt_t *p_pmt )
1760 {
1761     access_sys_t *p_sys = p_access->p_sys;
1762     int i, i_session_id;
1763     vlc_bool_t b_update = VLC_FALSE;
1764     vlc_bool_t b_needs_descrambling = CAPMTNeedsDescrambling( p_pmt );
1765
1766     for ( i = 0; i < MAX_PROGRAMS; i++ )
1767     {
1768         if ( p_sys->pp_selected_programs[i] != NULL
1769               && p_sys->pp_selected_programs[i]->i_program_number
1770                   == p_pmt->i_program_number )
1771         {
1772             b_update = VLC_TRUE;
1773
1774             if ( !b_needs_descrambling )
1775             {
1776                 dvbpsi_DeletePMT( p_pmt );
1777                 p_pmt = p_sys->pp_selected_programs[i];
1778                 p_sys->pp_selected_programs[i] = NULL;
1779             }
1780             else if( p_pmt != p_sys->pp_selected_programs[i] )
1781             {
1782                 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
1783                 p_sys->pp_selected_programs[i] = p_pmt;
1784             }
1785
1786             break;
1787         }
1788     }
1789
1790     if ( !b_update && b_needs_descrambling )
1791     {
1792         for ( i = 0; i < MAX_PROGRAMS; i++ )
1793         {
1794             if ( p_sys->pp_selected_programs[i] == NULL )
1795             {
1796                 p_sys->pp_selected_programs[i] = p_pmt;
1797                 break;
1798             }
1799         }
1800     }
1801
1802     if ( b_update || b_needs_descrambling )
1803     {
1804         for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
1805         {
1806             if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1807                     == RI_CONDITIONAL_ACCESS_SUPPORT )
1808             {
1809                 if ( b_update && b_needs_descrambling )
1810                     CAPMTUpdate( p_access, i_session_id, p_pmt );
1811                 else if ( b_update )
1812                     CAPMTDelete( p_access, i_session_id, p_pmt );
1813                 else
1814                     CAPMTAdd( p_access, i_session_id, p_pmt );
1815             }
1816         }
1817     }
1818
1819     if ( !b_needs_descrambling )
1820     {
1821         dvbpsi_DeletePMT( p_pmt );
1822     }
1823
1824     return VLC_SUCCESS;
1825 }
1826
1827 /*****************************************************************************
1828  * en50221_End :
1829  *****************************************************************************/
1830 void E_(en50221_End)( access_t * p_access )
1831 {
1832     access_sys_t *p_sys = p_access->p_sys;
1833     int i;
1834
1835     for ( i = 0; i < MAX_PROGRAMS; i++ )
1836     {
1837         if ( p_sys->pp_selected_programs[i] != NULL )
1838         {
1839             dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
1840         }
1841     }
1842
1843     /* TODO */
1844 }