]> git.sesse.net Git - vlc/blob - modules/access/dvb/en50221.c
* modules/access/dvb: Set the DVR device in non-blocking mode because it
[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
66 static void ResourceManagerOpen( access_t * p_access, int i_session_id );
67 static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
68 static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
69 static void DateTimeOpen( access_t * p_access, int i_session_id );
70 static void MMIOpen( access_t * p_access, int i_session_id );
71
72 /*****************************************************************************
73  * Utility functions
74  *****************************************************************************/
75 #define SIZE_INDICATOR 0x80
76
77 static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
78 {
79     *pi_length = *p_data++;
80
81     if ( (*pi_length & SIZE_INDICATOR) != 0 )
82     {
83         int l = *pi_length & ~SIZE_INDICATOR;
84         int i;
85
86         *pi_length = 0;
87         for ( i = 0; i < l; i++ )
88             *pi_length = (*pi_length << 8) | *p_data++;
89     }
90
91     return p_data;
92 }
93
94 static uint8_t *SetLength( uint8_t *p_data, int i_length )
95 {
96     uint8_t *p = p_data;
97
98     if ( i_length < 128 )
99     {
100         *p++ = i_length;
101     }
102     else if ( i_length < 256 )
103     {
104         *p++ = SIZE_INDICATOR | 0x1;
105         *p++ = i_length;
106     }
107     else if ( i_length < 65536 )
108     {
109         *p++ = SIZE_INDICATOR | 0x2;
110         *p++ = i_length >> 8;
111         *p++ = i_length & 0xff;
112     }
113     else if ( i_length < 16777216 )
114     {
115         *p++ = SIZE_INDICATOR | 0x3;
116         *p++ = i_length >> 16;
117         *p++ = (i_length >> 8) & 0xff;
118         *p++ = i_length & 0xff;
119     }
120     else
121     {
122         *p++ = SIZE_INDICATOR | 0x4;
123         *p++ = i_length >> 24;
124         *p++ = (i_length >> 16) & 0xff;
125         *p++ = (i_length >> 8) & 0xff;
126         *p++ = i_length & 0xff;
127     }
128
129     return p;
130 }
131
132
133 /*
134  * Transport layer
135  */
136
137 #define MAX_TPDU_SIZE  2048
138 #define MAX_TPDU_DATA  (MAX_TPDU_SIZE - 4)
139
140 #define DATA_INDICATOR 0x80
141
142 #define T_SB           0x80
143 #define T_RCV          0x81
144 #define T_CREATE_TC    0x82
145 #define T_CTC_REPLY    0x83
146 #define T_DELETE_TC    0x84
147 #define T_DTC_REPLY    0x85
148 #define T_REQUEST_TC   0x86
149 #define T_NEW_TC       0x87
150 #define T_TC_ERROR     0x88
151 #define T_DATA_LAST    0xA0
152 #define T_DATA_MORE    0xA1
153
154 static void Dump( vlc_bool_t b_outgoing, uint8_t *p_data, int i_size )
155 {
156 #ifdef DEBUG_TPDU
157     int i;
158 #define MAX_DUMP 256
159     fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
160     for ( i = 0; i < i_size && i < MAX_DUMP; i++)
161         fprintf(stderr, "%02X ", p_data[i]);
162     fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
163 #endif
164 }
165
166 /*****************************************************************************
167  * TPDUSend
168  *****************************************************************************/
169 static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
170                      const uint8_t *p_content, int i_length )
171 {
172     access_sys_t *p_sys = p_access->p_sys;
173     uint8_t i_tcid = i_slot + 1;
174     uint8_t p_data[MAX_TPDU_SIZE];
175     int i_size;
176
177     i_size = 0;
178     p_data[0] = i_slot;
179     p_data[1] = i_tcid;
180     p_data[2] = i_tag;
181
182     switch ( i_tag )
183     {
184     case T_RCV:
185     case T_CREATE_TC:
186     case T_CTC_REPLY:
187     case T_DELETE_TC:
188     case T_DTC_REPLY:
189     case T_REQUEST_TC:
190         p_data[3] = 1; /* length */
191         p_data[4] = i_tcid;
192         i_size = 5;
193         break;
194
195     case T_NEW_TC:
196     case T_TC_ERROR:
197         p_data[3] = 2; /* length */
198         p_data[4] = i_tcid;
199         p_data[5] = p_content[0];
200         i_size = 6;
201         break;
202
203     case T_DATA_LAST:
204     case T_DATA_MORE:
205     {
206         /* i_length <= MAX_TPDU_DATA */
207         uint8_t *p = p_data + 3;
208         p = SetLength( p, i_length + 1 );
209         *p++ = i_tcid;
210
211         if ( i_length )
212             memcpy( p, p_content, i_length );
213             i_size = i_length + (p - p_data);
214         }
215         break;
216
217     default:
218         break;
219     }
220     Dump( VLC_TRUE, p_data, i_size );
221
222     if ( write( p_sys->i_ca_handle, p_data, i_size ) != i_size )
223     {
224         msg_Err( p_access, "cannot write to CAM device (%s)",
225                  strerror(errno) );
226         return VLC_EGENERIC;
227     }
228
229     return VLC_SUCCESS;
230 }
231
232
233 /*****************************************************************************
234  * TPDURecv
235  *****************************************************************************/
236 #define CAM_READ_TIMEOUT  3500 // ms
237
238 static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
239                      uint8_t *p_data, int *pi_size )
240 {
241     access_sys_t *p_sys = p_access->p_sys;
242     uint8_t i_tcid = i_slot + 1;
243     int i_size;
244     struct pollfd pfd[1];
245
246     pfd[0].fd = p_sys->i_ca_handle;
247     pfd[0].events = POLLIN;
248     if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
249     {
250         msg_Err( p_access, "cannot poll from CAM device" );
251         return VLC_EGENERIC;
252     }
253
254     if ( pi_size == NULL )
255     {
256         p_data = malloc( MAX_TPDU_SIZE );
257     }
258
259     for ( ; ; )
260     {
261         i_size = read( p_sys->i_ca_handle, p_data, MAX_TPDU_SIZE );
262
263         if ( i_size >= 0 || errno != EINTR )
264             break;
265     }
266
267     if ( i_size < 5 )
268     {
269         msg_Err( p_access, "cannot read from CAM device (%d:%s)", i_size,
270                  strerror(errno) );
271         return VLC_EGENERIC;
272     }
273
274     if ( p_data[1] != i_tcid )
275     {
276         msg_Err( p_access, "invalid read from CAM device (%d instead of %d)",
277                  p_data[1], i_tcid );
278         return VLC_EGENERIC;
279     }
280
281     *pi_tag = p_data[2];
282     p_sys->pb_tc_has_data[i_slot] = (i_size >= 4
283                                       && p_data[i_size - 4] == T_SB
284                                       && p_data[i_size - 3] == 2
285                                       && (p_data[i_size - 1] & DATA_INDICATOR))
286                                         ?  VLC_TRUE : VLC_FALSE;
287
288     Dump( VLC_FALSE, p_data, i_size );
289
290     if ( pi_size == NULL )
291         free( p_data );
292     else
293         *pi_size = i_size;
294
295     return VLC_SUCCESS;
296 }
297
298
299 /*
300  * Session layer
301  */
302
303 #define ST_SESSION_NUMBER           0x90
304 #define ST_OPEN_SESSION_REQUEST     0x91
305 #define ST_OPEN_SESSION_RESPONSE    0x92
306 #define ST_CREATE_SESSION           0x93
307 #define ST_CREATE_SESSION_RESPONSE  0x94
308 #define ST_CLOSE_SESSION_REQUEST    0x95
309 #define ST_CLOSE_SESSION_RESPONSE   0x96
310
311 #define SS_OK             0x00
312 #define SS_NOT_ALLOCATED  0xF0
313
314 #define RI_RESOURCE_MANAGER            0x00010041
315 #define RI_APPLICATION_INFORMATION     0x00020041
316 #define RI_CONDITIONAL_ACCESS_SUPPORT  0x00030041
317 #define RI_HOST_CONTROL                0x00200041
318 #define RI_DATE_TIME                   0x00240041
319 #define RI_MMI                         0x00400041
320
321 static int ResourceIdToInt( uint8_t *p_data )
322 {
323     return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
324             | ((int)p_data[2] << 8) | p_data[3];
325 }
326
327 /*****************************************************************************
328  * SPDUSend
329  *****************************************************************************/
330 static int SPDUSend( access_t * p_access, int i_session_id,
331                      uint8_t *p_data, int i_size )
332 {
333     access_sys_t *p_sys = p_access->p_sys;
334     uint8_t *p_spdu = malloc( i_size + 4 );
335     uint8_t *p = p_spdu;
336     uint8_t i_tag;
337     uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
338
339     *p++ = ST_SESSION_NUMBER;
340     *p++ = 0x02;
341     *p++ = (i_session_id >> 8);
342     *p++ = i_session_id & 0xff;
343
344     memcpy( p, p_data, i_size );
345
346     i_size += 4;
347     p = p_spdu;
348
349     while ( i_size > 0 )
350     {
351         if ( i_size > MAX_TPDU_DATA )
352         {
353             if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
354                            MAX_TPDU_DATA ) != VLC_SUCCESS )
355             {
356                 msg_Err( p_access, "couldn't send TPDU on session %d",
357                          i_session_id );
358                 free( p_spdu );
359                 return VLC_EGENERIC;
360             }
361             p += MAX_TPDU_DATA;
362             i_size -= MAX_TPDU_DATA;
363         }
364         else
365         {
366             if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
367                     != VLC_SUCCESS )
368             {
369                 msg_Err( p_access, "couldn't send TPDU on session %d",
370                          i_session_id );
371                 free( p_spdu );
372                 return VLC_EGENERIC;
373             }
374             i_size = 0;
375         }
376
377         if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
378                || i_tag != T_SB )
379         {
380             msg_Err( p_access, "couldn't recv TPDU on session %d",
381                      i_session_id );
382             free( p_spdu );
383             return VLC_EGENERIC;
384         }
385     }
386
387     free( p_spdu );
388     return VLC_SUCCESS;
389 }
390
391 /*****************************************************************************
392  * SessionOpen
393  *****************************************************************************/
394 static void SessionOpen( access_t * p_access, uint8_t i_slot,
395                          uint8_t *p_spdu, int i_size )
396 {
397     access_sys_t *p_sys = p_access->p_sys;
398     int i_session_id;
399     int i_resource_id = ResourceIdToInt( &p_spdu[2] );
400     uint8_t p_response[16];
401     int i_status = SS_NOT_ALLOCATED;
402     uint8_t i_tag;
403
404     for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
405     {
406         if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
407             break;
408     }
409     if ( i_session_id == MAX_SESSIONS )
410     {
411         msg_Err( p_access, "too many sessions !" );
412         return;
413     }
414     p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
415     p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
416     p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
417     p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
418
419     if ( i_resource_id == RI_RESOURCE_MANAGER
420           || i_resource_id == RI_APPLICATION_INFORMATION
421           || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
422           || i_resource_id == RI_DATE_TIME
423           || i_resource_id == RI_MMI )
424     {
425         i_status = SS_OK;
426     }
427
428     p_response[0] = ST_OPEN_SESSION_RESPONSE;
429     p_response[1] = 0x7;
430     p_response[2] = i_status;
431     p_response[3] = p_spdu[2];
432     p_response[4] = p_spdu[3];
433     p_response[5] = p_spdu[4];
434     p_response[6] = p_spdu[5];
435     p_response[7] = i_session_id >> 8;
436     p_response[8] = i_session_id & 0xff;
437
438     if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) !=
439             VLC_SUCCESS )
440     {
441         msg_Err( p_access,
442                  "SessionOpen: couldn't send TPDU on slot %d", i_slot );
443         return;
444     }
445     if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
446     {
447         msg_Err( p_access,
448                  "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
449         return;
450     }
451
452     switch ( i_resource_id )
453     {
454     case RI_RESOURCE_MANAGER:
455         ResourceManagerOpen( p_access, i_session_id ); break; 
456     case RI_APPLICATION_INFORMATION:
457         ApplicationInformationOpen( p_access, i_session_id ); break; 
458     case RI_CONDITIONAL_ACCESS_SUPPORT:
459         ConditionalAccessOpen( p_access, i_session_id ); break; 
460     case RI_DATE_TIME:
461         DateTimeOpen( p_access, i_session_id ); break; 
462     case RI_MMI:
463         MMIOpen( p_access, i_session_id ); break; 
464
465     case RI_HOST_CONTROL:
466     default:
467         msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
468         p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
469     }
470 }
471
472 /*****************************************************************************
473  * SessionClose
474  *****************************************************************************/
475 static void SessionClose( access_t * p_access, int i_session_id )
476 {
477     access_sys_t *p_sys = p_access->p_sys;
478     uint8_t p_response[16];
479     uint8_t i_tag;
480     uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
481
482     if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
483         p_sys->p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
484     p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
485
486     p_response[0] = ST_CLOSE_SESSION_RESPONSE;
487     p_response[1] = 0x3;
488     p_response[2] = SS_OK;
489     p_response[3] = i_session_id >> 8;
490     p_response[4] = i_session_id & 0xff;
491
492     if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
493             VLC_SUCCESS )
494     {
495         msg_Err( p_access,
496                  "SessionOpen: couldn't send TPDU on slot %d", i_slot );
497         return;
498     }
499     if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
500     {
501         msg_Err( p_access,
502                  "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
503         return;
504     }
505 }
506
507 /*****************************************************************************
508  * SPDUHandle
509  *****************************************************************************/
510 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
511                         uint8_t *p_spdu, int i_size )
512 {
513     access_sys_t *p_sys = p_access->p_sys;
514     int i_session_id;
515
516     switch ( p_spdu[0] )
517     {
518     case ST_SESSION_NUMBER:
519         if ( i_size <= 4 )
520             return;
521         i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
522         p_sys->p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
523                                                        p_spdu + 4, i_size - 4 );
524         break;
525
526     case ST_OPEN_SESSION_REQUEST:
527         if ( i_size != 6 || p_spdu[1] != 0x4 )
528             return;
529         SessionOpen( p_access, i_slot, p_spdu, i_size );
530         break;
531
532     case ST_CLOSE_SESSION_REQUEST:
533         i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
534         SessionClose( p_access, i_session_id );
535         break;
536
537     default:
538         break;
539     }
540 }
541
542
543 /*
544  * Application layer
545  */
546
547 #define AOT_NONE                    0x000000
548 #define AOT_PROFILE_ENQ             0x9F8010
549 #define AOT_PROFILE                 0x9F8011
550 #define AOT_PROFILE_CHANGE          0x9F8012
551 #define AOT_APPLICATION_INFO_ENQ    0x9F8020
552 #define AOT_APPLICATION_INFO        0x9F8021
553 #define AOT_ENTER_MENU              0x9F8022
554 #define AOT_CA_INFO_ENQ             0x9F8030
555 #define AOT_CA_INFO                 0x9F8031
556 #define AOT_CA_PMT                  0x9F8032
557 #define AOT_CA_PMT_REPLY            0x9F8033
558 #define AOT_TUNE                    0x9F8400
559 #define AOT_REPLACE                 0x9F8401
560 #define AOT_CLEAR_REPLACE           0x9F8402
561 #define AOT_ASK_RELEASE             0x9F8403
562 #define AOT_DATE_TIME_ENQ           0x9F8440
563 #define AOT_DATE_TIME               0x9F8441
564 #define AOT_CLOSE_MMI               0x9F8800
565 #define AOT_DISPLAY_CONTROL         0x9F8801
566 #define AOT_DISPLAY_REPLY           0x9F8802
567 #define AOT_TEXT_LAST               0x9F8803
568 #define AOT_TEXT_MORE               0x9F8804
569 #define AOT_KEYPAD_CONTROL          0x9F8805
570 #define AOT_KEYPRESS                0x9F8806
571 #define AOT_ENQ                     0x9F8807
572 #define AOT_ANSW                    0x9F8808
573 #define AOT_MENU_LAST               0x9F8809
574 #define AOT_MENU_MORE               0x9F880A
575 #define AOT_MENU_ANSW               0x9F880B
576 #define AOT_LIST_LAST               0x9F880C
577 #define AOT_LIST_MORE               0x9F880D
578 #define AOT_SUBTITLE_SEGMENT_LAST   0x9F880E
579 #define AOT_SUBTITLE_SEGMENT_MORE   0x9F880F
580 #define AOT_DISPLAY_MESSAGE         0x9F8810
581 #define AOT_SCENE_END_MARK          0x9F8811
582 #define AOT_SCENE_DONE              0x9F8812
583 #define AOT_SCENE_CONTROL           0x9F8813
584 #define AOT_SUBTITLE_DOWNLOAD_LAST  0x9F8814
585 #define AOT_SUBTITLE_DOWNLOAD_MORE  0x9F8815
586 #define AOT_FLUSH_DOWNLOAD          0x9F8816
587 #define AOT_DOWNLOAD_REPLY          0x9F8817
588 #define AOT_COMMS_CMD               0x9F8C00
589 #define AOT_CONNECTION_DESCRIPTOR   0x9F8C01
590 #define AOT_COMMS_REPLY             0x9F8C02
591 #define AOT_COMMS_SEND_LAST         0x9F8C03
592 #define AOT_COMMS_SEND_MORE         0x9F8C04
593 #define AOT_COMMS_RCV_LAST          0x9F8C05
594 #define AOT_COMMS_RCV_MORE          0x9F8C06
595
596 /*****************************************************************************
597  * APDUGetTag
598  *****************************************************************************/
599 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
600 {
601     if ( i_size >= 3 )
602     {
603         int i, t = 0;
604         for ( i = 0; i < 3; i++ )
605             t = (t << 8) | *p_apdu++;
606         return t;
607     }
608
609     return AOT_NONE;
610 }
611
612 /*****************************************************************************
613  * APDUGetLength
614  *****************************************************************************/
615 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
616 {
617     return GetLength( &p_apdu[3], pi_size );
618 }
619
620 /*****************************************************************************
621  * APDUSend
622  *****************************************************************************/
623 static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
624                      uint8_t *p_data, int i_size )
625 {
626     uint8_t *p_apdu = malloc( i_size + 12 );
627     uint8_t *p = p_apdu;
628     int i_ret;
629
630     *p++ = (i_tag >> 16);
631     *p++ = (i_tag >> 8) & 0xff;
632     *p++ = i_tag & 0xff;
633     p = SetLength( p, i_size );
634     if ( i_size )
635         memcpy( p, p_data, i_size );
636
637     i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
638     free( p_apdu );
639     return i_ret;
640 }
641
642 /*
643  * Resource Manager
644  */
645
646 /*****************************************************************************
647  * ResourceManagerHandle
648  *****************************************************************************/
649 static void ResourceManagerHandle( access_t * p_access, int i_session_id,
650                                    uint8_t *p_apdu, int i_size )
651 {
652     int i_tag = APDUGetTag( p_apdu, i_size );
653
654     switch ( i_tag )
655     {
656     case AOT_PROFILE_ENQ:
657     {
658         int resources[] = { htonl(RI_RESOURCE_MANAGER),
659                             htonl(RI_APPLICATION_INFORMATION),
660                             htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
661                             htonl(RI_DATE_TIME),
662                             htonl(RI_MMI)
663                           };
664         APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
665                   sizeof(resources) );
666         break;
667     }
668     case AOT_PROFILE:
669         APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
670         break;
671
672     default:
673         msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
674                  i_tag );
675     }
676 }
677
678 /*****************************************************************************
679  * ResourceManagerOpen
680  *****************************************************************************/
681 static void ResourceManagerOpen( access_t * p_access, int i_session_id )
682 {
683     access_sys_t *p_sys = p_access->p_sys;
684
685     msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
686
687     p_sys->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
688
689     APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
690 }
691
692 /*
693  * Application Information
694  */
695
696 /*****************************************************************************
697  * ApplicationInformationHandle
698  *****************************************************************************/
699 static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
700                                           uint8_t *p_apdu, int i_size )
701 {
702     int i_tag = APDUGetTag( p_apdu, i_size );
703
704     switch ( i_tag )
705     {
706     case AOT_APPLICATION_INFO:
707     {
708         int i_type, i_manufacturer, i_code;
709         int l = 0;
710         uint8_t *d = APDUGetLength( p_apdu, &l );
711
712         if ( l < 4 ) break;
713         p_apdu[l + 4] = '\0';
714
715         i_type = *d++;
716         i_manufacturer = ((int)d[0] << 8) | d[1];
717         d += 2;
718         i_code = ((int)d[0] << 8) | d[1];
719         d += 2;
720         d = GetLength( d, &l );
721         d[l] = '\0';
722         msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
723                   d, i_type, i_manufacturer, i_code );
724         break;
725     }
726     default:
727         msg_Err( p_access,
728                  "unexpected tag in ApplicationInformationHandle (0x%x)",
729                  i_tag );
730     }
731 }
732
733 /*****************************************************************************
734  * ApplicationInformationOpen
735  *****************************************************************************/
736 static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
737 {
738     access_sys_t *p_sys = p_access->p_sys;
739
740     msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
741
742     p_sys->p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
743
744     APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
745 }
746
747 /*
748  * Conditional Access
749  */
750
751 #define MAX_CASYSTEM_IDS 16
752
753 typedef struct
754 {
755     uint16_t pi_system_ids[MAX_CASYSTEM_IDS + 1];
756 } system_ids_t;
757
758 static vlc_bool_t CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
759 {
760     int i = 0;
761     while ( p_ids->pi_system_ids[i] )
762     {
763         if ( p_ids->pi_system_ids[i] == i_id )
764             return VLC_TRUE;
765         i++;
766     }
767
768     return VLC_FALSE;
769 }
770
771 /*****************************************************************************
772  * CAPMTNeedsDescrambling
773  *****************************************************************************/
774 static vlc_bool_t CAPMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
775 {
776     dvbpsi_descriptor_t *p_dr;
777     dvbpsi_pmt_es_t *p_es;
778
779     for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
780     {
781         if( p_dr->i_tag == 0x9 )
782         {
783             return VLC_TRUE;
784         }
785     }
786     
787     for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
788     {
789         for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
790              p_dr = p_dr->p_next )
791         {
792             if( p_dr->i_tag == 0x9 )
793             {
794                 return VLC_TRUE;
795             }
796         }
797     }
798
799     return VLC_FALSE;
800 }
801
802 /*****************************************************************************
803  * CAPMTBuild
804  *****************************************************************************/
805 static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr )
806 {
807     int i_cad_size = 0;
808
809     while ( p_dr != NULL )
810     {
811         if( p_dr->i_tag == 0x9 )
812         {
813             uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
814                                     | p_dr->p_data[1];
815             if ( CheckSystemID( p_ids, i_sysid ) )
816                 i_cad_size += p_dr->i_length + 2;
817         }
818         p_dr = p_dr->p_next;
819     }
820
821     return i_cad_size;
822 }
823
824 static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt,
825                              uint16_t i_program_number, uint8_t i_version,
826                              int i_size, dvbpsi_descriptor_t *p_dr,
827                              uint8_t i_cmd )
828 {
829     uint8_t *p_data;
830
831     if ( i_size )
832         p_data = malloc( 7 + i_size );
833     else
834         p_data = malloc( 6 );
835
836     p_data[0] = i_list_mgt;
837     p_data[1] = i_program_number >> 8;
838     p_data[2] = i_program_number & 0xff;
839     p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
840
841     if ( i_size )
842     {
843         int i;
844
845         p_data[4] = (i_size + 1) >> 8;
846         p_data[5] = (i_size + 1) & 0xff;
847         p_data[6] = i_cmd;
848         i = 7;
849
850         while ( p_dr != NULL )
851         {
852             if( p_dr->i_tag == 0x9 )
853             {
854                 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
855                                     | p_dr->p_data[1];
856                 if ( CheckSystemID( p_ids, i_sysid ) )
857                 {
858                     p_data[i] = 0x9;
859                     p_data[i + 1] = p_dr->i_length;
860                     memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
861                     i += p_dr->i_length + 2;
862                 }
863             }
864             p_dr = p_dr->p_next;
865         }
866     }
867     else
868     {
869         p_data[4] = 0;
870         p_data[5] = 0;
871     }
872
873     return p_data;
874 }
875
876 static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt,
877                          int i_capmt_size, uint8_t i_type, uint16_t i_pid,
878                          int i_size, dvbpsi_descriptor_t *p_dr,
879                          uint8_t i_cmd )
880 {
881     uint8_t *p_data;
882     int i;
883     
884     if ( i_size )
885         p_data = realloc( p_capmt, i_capmt_size + 6 + i_size );
886     else
887         p_data = realloc( p_capmt, i_capmt_size + 5 );
888
889     i = i_capmt_size;
890
891     p_data[i] = i_type;
892     p_data[i + 1] = i_pid >> 8;
893     p_data[i + 2] = i_pid & 0xff;
894
895     if ( i_size )
896     {
897         p_data[i + 3] = (i_size + 1) >> 8;
898         p_data[i + 4] = (i_size + 1) & 0xff;
899         p_data[i + 5] = i_cmd;
900         i += 6;
901
902         while ( p_dr != NULL )
903         {
904             if( p_dr->i_tag == 0x9 )
905             {
906                 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
907                                     | p_dr->p_data[1];
908                 if ( CheckSystemID( p_ids, i_sysid ) )
909                 {
910                     p_data[i] = 0x9;
911                     p_data[i + 1] = p_dr->i_length;
912                     memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
913                     i += p_dr->i_length + 2;
914                 }
915             }
916             p_dr = p_dr->p_next;
917         }
918     }
919     else
920     {
921         p_data[i + 3] = 0;
922         p_data[i + 4] = 0;
923     }
924
925     return p_data;
926 }
927
928 static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id,
929                             dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt,
930                             uint8_t i_cmd, int *pi_capmt_size )
931 {
932     access_sys_t *p_sys = p_access->p_sys;
933     system_ids_t *p_ids =
934         (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
935     dvbpsi_pmt_es_t *p_es;
936     int i_cad_size, i_cad_program_size;
937     uint8_t *p_capmt;
938
939     i_cad_size = i_cad_program_size =
940             GetCADSize( p_ids, p_pmt->p_first_descriptor );
941     for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
942     {
943         i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor );
944     }
945
946     if ( !i_cad_size )
947     {
948         msg_Warn( p_access,
949                   "no compatible scrambling system for SID %d on session %d",
950                   p_pmt->i_program_number, i_session_id );
951         *pi_capmt_size = 0;
952         return NULL;
953     }
954
955     p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number,
956                            p_pmt->i_version, i_cad_program_size,
957                            p_pmt->p_first_descriptor, i_cmd );
958
959     if ( i_cad_program_size )
960         *pi_capmt_size = 7 + i_cad_program_size;
961     else
962         *pi_capmt_size = 6;
963
964     for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
965     {
966         i_cad_size = GetCADSize( p_ids, p_es->p_first_descriptor );
967
968         if ( i_cad_size || i_cad_program_size )
969         {
970             p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type,
971                                p_es->i_pid, i_cad_size,
972                                p_es->p_first_descriptor, i_cmd );
973             if ( i_cad_size )
974                 *pi_capmt_size += 6 + i_cad_size;
975             else
976                 *pi_capmt_size += 5;
977         }
978     }
979
980     return p_capmt;
981 }
982
983 /*****************************************************************************
984  * CAPMTFirst
985  *****************************************************************************/
986 static void CAPMTFirst( access_t * p_access, int i_session_id,
987                         dvbpsi_pmt_t *p_pmt )
988 {
989     uint8_t *p_capmt;
990     int i_capmt_size;
991
992     msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d",
993              p_pmt->i_program_number, i_session_id );
994
995     p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
996                           0x3 /* only */, 0x1 /* ok_descrambling */,
997                           &i_capmt_size );
998
999     if ( i_capmt_size )
1000         APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1001 }
1002
1003 /*****************************************************************************
1004  * CAPMTAdd
1005  *****************************************************************************/
1006 static void CAPMTAdd( access_t * p_access, int i_session_id,
1007                       dvbpsi_pmt_t *p_pmt )
1008 {
1009     uint8_t *p_capmt;
1010     int i_capmt_size;
1011
1012     msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
1013              p_pmt->i_program_number, i_session_id );
1014
1015     p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1016                           0x4 /* add */, 0x1 /* ok_descrambling */,
1017                           &i_capmt_size );
1018
1019     if ( i_capmt_size )
1020         APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1021 }
1022
1023 /*****************************************************************************
1024  * CAPMTUpdate
1025  *****************************************************************************/
1026 static void CAPMTUpdate( access_t * p_access, int i_session_id,
1027                          dvbpsi_pmt_t *p_pmt )
1028 {
1029     uint8_t *p_capmt;
1030     int i_capmt_size;
1031
1032     msg_Dbg( p_access, "updating CAPMT for SID %d on session %d",
1033              p_pmt->i_program_number, i_session_id );
1034
1035     p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1036                           0x5 /* update */, 0x1 /* ok_descrambling */,
1037                           &i_capmt_size );
1038
1039     if ( i_capmt_size )
1040         APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1041 }
1042
1043 /*****************************************************************************
1044  * CAPMTDelete
1045  *****************************************************************************/
1046 static void CAPMTDelete( access_t * p_access, int i_session_id,
1047                          dvbpsi_pmt_t *p_pmt )
1048 {
1049     uint8_t *p_capmt;
1050     int i_capmt_size;
1051
1052     msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d",
1053              p_pmt->i_program_number, i_session_id );
1054
1055     p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1056                           0x5 /* update */, 0x4 /* not selected */,
1057                           &i_capmt_size );
1058
1059     if ( i_capmt_size )
1060         APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1061 }
1062
1063 /*****************************************************************************
1064  * ConditionalAccessHandle
1065  *****************************************************************************/
1066 static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
1067                                      uint8_t *p_apdu, int i_size )
1068 {
1069     access_sys_t *p_sys = p_access->p_sys;
1070     system_ids_t *p_ids =
1071         (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1072     int i_tag = APDUGetTag( p_apdu, i_size );
1073
1074     switch ( i_tag )
1075     {
1076     case AOT_CA_INFO:
1077     {
1078         vlc_bool_t b_inited = VLC_FALSE;
1079         int i;
1080         int l = 0;
1081         uint8_t *d = APDUGetLength( p_apdu, &l );
1082         msg_Dbg( p_access, "CA system IDs supported by the application :" );
1083
1084         for ( i = 0; i < l / 2; i++ )
1085         {
1086             p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
1087             d += 2;
1088             msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] );
1089         }
1090         p_ids->pi_system_ids[i] = 0;
1091
1092         for ( i = 0; i < MAX_PROGRAMS; i++ )
1093         {
1094             if ( p_sys->pp_selected_programs[i] != NULL )
1095             {
1096                 if ( b_inited )
1097                     CAPMTAdd( p_access, i_session_id,
1098                               p_sys->pp_selected_programs[i] );
1099                 else
1100                     CAPMTFirst( p_access, i_session_id,
1101                                 p_sys->pp_selected_programs[i] );
1102                 b_inited = VLC_TRUE;
1103             }
1104         }
1105         break;
1106     }
1107
1108     default:
1109         msg_Err( p_access,
1110                  "unexpected tag in ConditionalAccessHandle (0x%x)",
1111                  i_tag );
1112     }
1113 }
1114
1115 /*****************************************************************************
1116  * ConditionalAccessOpen
1117  *****************************************************************************/
1118 static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
1119 {
1120     access_sys_t *p_sys = p_access->p_sys;
1121
1122     msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
1123
1124     p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
1125     p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(system_ids_t));
1126     memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0,
1127             sizeof(system_ids_t) );
1128
1129     APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
1130 }
1131
1132 /*
1133  * Date Time
1134  */
1135
1136 typedef struct
1137 {
1138     int i_interval;
1139     mtime_t i_last;
1140 } date_time_t;
1141
1142 /*****************************************************************************
1143  * DateTimeSend
1144  *****************************************************************************/
1145 static void DateTimeSend( access_t * p_access, int i_session_id )
1146 {
1147     access_sys_t *p_sys = p_access->p_sys;
1148     date_time_t *p_date =
1149         (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1150
1151     time_t t = time(NULL);
1152     struct tm tm_gmt;
1153     struct tm tm_loc;
1154
1155     if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
1156     {
1157         int Y = tm_gmt.tm_year;
1158         int M = tm_gmt.tm_mon + 1;
1159         int D = tm_gmt.tm_mday;
1160         int L = (M == 1 || M == 2) ? 1 : 0;
1161         int MJD = 14956 + D + (int)((Y - L) * 365.25)
1162                     + (int)((M + 1 + L * 12) * 30.6001);
1163         uint8_t p_response[7];
1164
1165 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
1166
1167         p_response[0] = htons(MJD) >> 8;
1168         p_response[1] = htons(MJD) & 0xff;
1169         p_response[2] = DEC2BCD(tm_gmt.tm_hour);
1170         p_response[3] = DEC2BCD(tm_gmt.tm_min);
1171         p_response[4] = DEC2BCD(tm_gmt.tm_sec);
1172         p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
1173         p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
1174
1175         APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
1176
1177         p_date->i_last = mdate();
1178     }
1179 }
1180
1181 /*****************************************************************************
1182  * DateTimeHandle
1183  *****************************************************************************/
1184 static void DateTimeHandle( access_t * p_access, int i_session_id,
1185                             uint8_t *p_apdu, int i_size )
1186 {
1187     access_sys_t *p_sys = p_access->p_sys;
1188     date_time_t *p_date =
1189         (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1190
1191     int i_tag = APDUGetTag( p_apdu, i_size );
1192
1193     switch ( i_tag )
1194     {
1195     case AOT_DATE_TIME_ENQ:
1196     {
1197         int l;
1198         const uint8_t *d = APDUGetLength( p_apdu, &l );
1199
1200         if ( l > 0 )
1201         {
1202             p_date->i_interval = *d;
1203             msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
1204                      p_date->i_interval );
1205         }
1206         else
1207             p_date->i_interval = 0;
1208
1209         DateTimeSend( p_access, i_session_id );
1210         break;
1211     }
1212     default:
1213         msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
1214     }
1215 }
1216
1217 /*****************************************************************************
1218  * DateTimeManage
1219  *****************************************************************************/
1220 static void DateTimeManage( access_t * p_access, int i_session_id )
1221 {
1222     access_sys_t *p_sys = p_access->p_sys;
1223     date_time_t *p_date =
1224         (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1225
1226     if ( p_date->i_interval
1227           && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
1228     {
1229         DateTimeSend( p_access, i_session_id );
1230     }
1231 }
1232
1233 /*****************************************************************************
1234  * DateTimeOpen
1235  *****************************************************************************/
1236 static void DateTimeOpen( access_t * p_access, int i_session_id )
1237 {
1238     access_sys_t *p_sys = p_access->p_sys;
1239
1240     msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
1241
1242     p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
1243     p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
1244     p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t));
1245     memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
1246
1247     DateTimeSend( p_access, i_session_id );
1248 }
1249
1250 /*
1251  * MMI
1252  */
1253
1254 /*****************************************************************************
1255  * MMIHandle
1256  *****************************************************************************/
1257 static void MMIHandle( access_t * p_access, int i_session_id,
1258                             uint8_t *p_apdu, int i_size )
1259 {
1260     int i_tag = APDUGetTag( p_apdu, i_size );
1261
1262     switch ( i_tag )
1263     {
1264     default:
1265         msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
1266     }
1267 }
1268
1269 /*****************************************************************************
1270  * MMIOpen
1271  *****************************************************************************/
1272 static void MMIOpen( access_t * p_access, int i_session_id )
1273 {
1274     access_sys_t *p_sys = p_access->p_sys;
1275
1276     msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
1277
1278     p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
1279 }
1280
1281
1282 /*
1283  * Hardware handling
1284  */
1285
1286 /*****************************************************************************
1287  * InitSlot: Open the transport layer
1288  *****************************************************************************/
1289 #define MAX_TC_RETRIES 20
1290
1291 static int InitSlot( access_t * p_access, int i_slot )
1292 {
1293     access_sys_t *p_sys = p_access->p_sys;
1294     int i;
1295
1296     if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1297             != VLC_SUCCESS )
1298     {
1299         msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
1300                  i_slot );
1301         return VLC_EGENERIC;
1302     }
1303
1304     /* This is out of the spec */
1305     for ( i = 0; i < MAX_TC_RETRIES; i++ )
1306     {
1307         uint8_t i_tag;
1308         if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
1309               && i_tag == T_CTC_REPLY )
1310         {
1311             p_sys->pb_active_slot[i_slot] = VLC_TRUE;
1312             break;
1313         }
1314
1315         if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1316                 != VLC_SUCCESS )
1317         {
1318             msg_Err( p_access,
1319                      "en50221_Init: couldn't send TPDU on slot %d",
1320                      i_slot );
1321             continue;
1322         }
1323     }
1324     if ( p_sys->pb_active_slot[i_slot] )
1325     {
1326         p_sys->i_ca_timeout = 100000;
1327         return VLC_SUCCESS;
1328     }
1329
1330     return VLC_EGENERIC;
1331 }
1332
1333
1334 /*
1335  * External entry points
1336  */
1337
1338 /*****************************************************************************
1339  * en50221_Poll : Poll the CAM for TPDUs
1340  *****************************************************************************/
1341 int E_(en50221_Poll)( access_t * p_access )
1342 {
1343     access_sys_t *p_sys = p_access->p_sys;
1344     int i_slot;
1345     int i_session_id;
1346
1347     for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1348     {
1349         uint8_t i_tag;
1350
1351         if ( !p_sys->pb_active_slot[i_slot] )
1352         {
1353             ca_slot_info_t sinfo;
1354             sinfo.num = i_slot;
1355             if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
1356             {
1357                 msg_Err( p_access, "en50221_Poll: couldn't get info on slot %d",
1358                          i_slot );
1359                 continue;
1360             }
1361
1362             if ( sinfo.flags & CA_CI_MODULE_READY )
1363             {
1364                 msg_Dbg( p_access, "en50221_Poll: slot %d is active",
1365                          i_slot );
1366                 p_sys->pb_active_slot[i_slot] = VLC_TRUE;
1367             }
1368             else
1369                 continue;
1370
1371             InitSlot( p_access, i_slot );
1372         }
1373
1374         if ( !p_sys->pb_tc_has_data[i_slot] )
1375         {
1376             if ( TPDUSend( p_access, i_slot, T_DATA_LAST, NULL, 0 ) !=
1377                     VLC_SUCCESS )
1378             {
1379                 msg_Err( p_access,
1380                          "en50221_Poll: couldn't send TPDU on slot %d",
1381                          i_slot );
1382                 continue;
1383             }
1384             if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) !=
1385                     VLC_SUCCESS )
1386             {
1387                 msg_Err( p_access,
1388                          "en50221_Poll: couldn't recv TPDU on slot %d",
1389                          i_slot );
1390                 continue;
1391             }
1392         }
1393
1394         while ( p_sys->pb_tc_has_data[i_slot] )
1395         {
1396             uint8_t p_tpdu[MAX_TPDU_SIZE];
1397             int i_size, i_session_size;
1398             uint8_t *p_session;
1399
1400             if ( TPDUSend( p_access, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
1401             {
1402                 msg_Err( p_access,
1403                          "en50221_Poll: couldn't send TPDU on slot %d",
1404                          i_slot );
1405                 continue;
1406             }
1407             if ( TPDURecv( p_access, i_slot, &i_tag, p_tpdu, &i_size ) !=
1408                     VLC_SUCCESS )
1409             {
1410                 msg_Err( p_access,
1411                          "en50221_Poll: couldn't recv TPDU on slot %d",
1412                          i_slot );
1413                 continue;
1414             }
1415
1416             p_session = GetLength( &p_tpdu[3], &i_session_size );
1417             if ( i_session_size <= 1 )
1418                 continue;
1419
1420             p_session++;
1421             i_session_size--;
1422
1423             if ( i_tag != T_DATA_LAST )
1424             {
1425                 msg_Err( p_access,
1426                          "en50221_Poll: fragmented TPDU not supported" );
1427                 break;
1428             }
1429
1430             SPDUHandle( p_access, i_slot, p_session, i_session_size );
1431         }
1432     }
1433
1434     for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
1435     {
1436         if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1437               && p_sys->p_sessions[i_session_id - 1].pf_manage )
1438         {
1439             p_sys->p_sessions[i_session_id - 1].pf_manage( p_access,
1440                                                            i_session_id );
1441         }
1442     }
1443
1444     return VLC_SUCCESS;
1445 }
1446
1447
1448 /*****************************************************************************
1449  * en50221_SetCAPMT :
1450  *****************************************************************************/
1451 int E_(en50221_SetCAPMT)( access_t * p_access, dvbpsi_pmt_t *p_pmt )
1452 {
1453     access_sys_t *p_sys = p_access->p_sys;
1454     int i, i_session_id;
1455     vlc_bool_t b_update = VLC_FALSE;
1456     vlc_bool_t b_needs_descrambling = CAPMTNeedsDescrambling( p_pmt );
1457
1458     for ( i = 0; i < MAX_PROGRAMS; i++ )
1459     {
1460         if ( p_sys->pp_selected_programs[i] != NULL
1461               && p_sys->pp_selected_programs[i]->i_program_number
1462                   == p_pmt->i_program_number )
1463         {
1464             b_update = VLC_TRUE;
1465
1466             if ( !b_needs_descrambling )
1467             {
1468                 dvbpsi_DeletePMT( p_pmt );
1469                 p_pmt = p_sys->pp_selected_programs[i];
1470                 p_sys->pp_selected_programs[i] = NULL;
1471             }
1472             else
1473             {
1474                 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
1475                 p_sys->pp_selected_programs[i] = p_pmt;
1476             }
1477
1478             break;
1479         }
1480     }
1481
1482     if ( !b_update && b_needs_descrambling )
1483     {
1484         for ( i = 0; i < MAX_PROGRAMS; i++ )
1485         {
1486             if ( p_sys->pp_selected_programs[i] == NULL )
1487             {
1488                 p_sys->pp_selected_programs[i] = p_pmt;
1489                 break;
1490             }
1491         }
1492     }
1493
1494     if ( b_update || b_needs_descrambling )
1495     {
1496         for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
1497         {
1498             if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1499                     == RI_CONDITIONAL_ACCESS_SUPPORT )
1500             {
1501                 if ( b_update && b_needs_descrambling )
1502                     CAPMTUpdate( p_access, i_session_id, p_pmt );
1503                 else if ( b_update )
1504                     CAPMTDelete( p_access, i_session_id, p_pmt );
1505                 else
1506                     CAPMTAdd( p_access, i_session_id, p_pmt );
1507             }
1508         }
1509     }
1510
1511     if ( !b_needs_descrambling )
1512     {
1513         dvbpsi_DeletePMT( p_pmt );
1514     }
1515
1516     return VLC_SUCCESS;
1517 }
1518
1519 /*****************************************************************************
1520  * en50221_End :
1521  *****************************************************************************/
1522 void E_(en50221_End)( access_t * p_access )
1523 {
1524     access_sys_t *p_sys = p_access->p_sys;
1525     int i;
1526
1527     for ( i = 0; i < MAX_PROGRAMS; i++ )
1528     {
1529         if ( p_sys->pp_selected_programs[i] != NULL )
1530         {
1531             dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
1532         }
1533     }
1534
1535     /* TODO */
1536 }