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