Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

en50221.c

00001 /*****************************************************************************
00002  * en50221.c : implementation of the transport, session and applications
00003  * layers of EN 50 221
00004  *****************************************************************************
00005  * Copyright (C) 2004 the VideoLAN team
00006  *
00007  * Authors: Christophe Massiot <[email protected]>
00008  * Based on code from libdvbci Copyright (C) 2000 Klaus Schmidinger
00009  *
00010  * This program is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA    02111, USA.
00023  *****************************************************************************/
00024 
00025 #include <vlc/vlc.h>
00026 #include <vlc/input.h>
00027 
00028 #include <sys/ioctl.h>
00029 #include <errno.h>
00030 
00031 #include <sys/types.h>
00032 #include <sys/stat.h>
00033 #include <fcntl.h>
00034 #include <time.h>
00035 #include <unistd.h>
00036 #include <sys/stat.h>
00037 #include <sys/poll.h>
00038 
00039 /* DVB Card Drivers */
00040 #include <linux/dvb/version.h>
00041 #include <linux/dvb/dmx.h>
00042 #include <linux/dvb/frontend.h>
00043 #include <linux/dvb/ca.h>
00044 
00045 /* Include dvbpsi headers */
00046 #ifdef HAVE_DVBPSI_DR_H
00047 #   include <dvbpsi/dvbpsi.h>
00048 #   include <dvbpsi/descriptor.h>
00049 #   include <dvbpsi/pat.h>
00050 #   include <dvbpsi/pmt.h>
00051 #   include <dvbpsi/dr.h>
00052 #   include <dvbpsi/psi.h>
00053 #else
00054 #   include "dvbpsi.h"
00055 #   include "descriptor.h"
00056 #   include "tables/pat.h"
00057 #   include "tables/pmt.h"
00058 #   include "descriptors/dr.h"
00059 #   include "psi.h"
00060 #endif
00061 
00062 #include "dvb.h"
00063 
00064 #undef DEBUG_TPDU
00065 
00066 static void ResourceManagerOpen( access_t * p_access, int i_session_id );
00067 static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
00068 static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
00069 static void DateTimeOpen( access_t * p_access, int i_session_id );
00070 static void MMIOpen( access_t * p_access, int i_session_id );
00071 
00072 /*****************************************************************************
00073  * Utility functions
00074  *****************************************************************************/
00075 #define SIZE_INDICATOR 0x80
00076 
00077 static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
00078 {
00079     *pi_length = *p_data++;
00080 
00081     if ( (*pi_length & SIZE_INDICATOR) != 0 )
00082     {
00083         int l = *pi_length & ~SIZE_INDICATOR;
00084         int i;
00085 
00086         *pi_length = 0;
00087         for ( i = 0; i < l; i++ )
00088             *pi_length = (*pi_length << 8) | *p_data++;
00089     }
00090 
00091     return p_data;
00092 }
00093 
00094 static uint8_t *SetLength( uint8_t *p_data, int i_length )
00095 {
00096     uint8_t *p = p_data;
00097 
00098     if ( i_length < 128 )
00099     {
00100         *p++ = i_length;
00101     }
00102     else if ( i_length < 256 )
00103     {
00104         *p++ = SIZE_INDICATOR | 0x1;
00105         *p++ = i_length;
00106     }
00107     else if ( i_length < 65536 )
00108     {
00109         *p++ = SIZE_INDICATOR | 0x2;
00110         *p++ = i_length >> 8;
00111         *p++ = i_length & 0xff;
00112     }
00113     else if ( i_length < 16777216 )
00114     {
00115         *p++ = SIZE_INDICATOR | 0x3;
00116         *p++ = i_length >> 16;
00117         *p++ = (i_length >> 8) & 0xff;
00118         *p++ = i_length & 0xff;
00119     }
00120     else
00121     {
00122         *p++ = SIZE_INDICATOR | 0x4;
00123         *p++ = i_length >> 24;
00124         *p++ = (i_length >> 16) & 0xff;
00125         *p++ = (i_length >> 8) & 0xff;
00126         *p++ = i_length & 0xff;
00127     }
00128 
00129     return p;
00130 }
00131 
00132 
00133 /*
00134  * Transport layer
00135  */
00136 
00137 #define MAX_TPDU_SIZE  2048
00138 #define MAX_TPDU_DATA  (MAX_TPDU_SIZE - 4)
00139 
00140 #define DATA_INDICATOR 0x80
00141 
00142 #define T_SB           0x80
00143 #define T_RCV          0x81
00144 #define T_CREATE_TC    0x82
00145 #define T_CTC_REPLY    0x83
00146 #define T_DELETE_TC    0x84
00147 #define T_DTC_REPLY    0x85
00148 #define T_REQUEST_TC   0x86
00149 #define T_NEW_TC       0x87
00150 #define T_TC_ERROR     0x88
00151 #define T_DATA_LAST    0xA0
00152 #define T_DATA_MORE    0xA1
00153 
00154 static void Dump( vlc_bool_t b_outgoing, uint8_t *p_data, int i_size )
00155 {
00156 #ifdef DEBUG_TPDU
00157     int i;
00158 #define MAX_DUMP 256
00159     fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
00160     for ( i = 0; i < i_size && i < MAX_DUMP; i++)
00161         fprintf(stderr, "%02X ", p_data[i]);
00162     fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
00163 #endif
00164 }
00165 
00166 /*****************************************************************************
00167  * TPDUSend
00168  *****************************************************************************/
00169 static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
00170                      const uint8_t *p_content, int i_length )
00171 {
00172     access_sys_t *p_sys = p_access->p_sys;
00173     uint8_t i_tcid = i_slot + 1;
00174     uint8_t p_data[MAX_TPDU_SIZE];
00175     int i_size;
00176 
00177     i_size = 0;
00178     p_data[0] = i_slot;
00179     p_data[1] = i_tcid;
00180     p_data[2] = i_tag;
00181 
00182     switch ( i_tag )
00183     {
00184     case T_RCV:
00185     case T_CREATE_TC:
00186     case T_CTC_REPLY:
00187     case T_DELETE_TC:
00188     case T_DTC_REPLY:
00189     case T_REQUEST_TC:
00190         p_data[3] = 1; /* length */
00191         p_data[4] = i_tcid;
00192         i_size = 5;
00193         break;
00194 
00195     case T_NEW_TC:
00196     case T_TC_ERROR:
00197         p_data[3] = 2; /* length */
00198         p_data[4] = i_tcid;
00199         p_data[5] = p_content[0];
00200         i_size = 6;
00201         break;
00202 
00203     case T_DATA_LAST:
00204     case T_DATA_MORE:
00205     {
00206         /* i_length <= MAX_TPDU_DATA */
00207         uint8_t *p = p_data + 3;
00208         p = SetLength( p, i_length + 1 );
00209         *p++ = i_tcid;
00210 
00211         if ( i_length )
00212             memcpy( p, p_content, i_length );
00213             i_size = i_length + (p - p_data);
00214         }
00215         break;
00216 
00217     default:
00218         break;
00219     }
00220     Dump( VLC_TRUE, p_data, i_size );
00221 
00222     if ( write( p_sys->i_ca_handle, p_data, i_size ) != i_size )
00223     {
00224         msg_Err( p_access, "cannot write to CAM device (%s)",
00225                  strerror(errno) );
00226         return VLC_EGENERIC;
00227     }
00228 
00229     return VLC_SUCCESS;
00230 }
00231 
00232 
00233 /*****************************************************************************
00234  * TPDURecv
00235  *****************************************************************************/
00236 #define CAM_READ_TIMEOUT  3500 // ms
00237 
00238 static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
00239                      uint8_t *p_data, int *pi_size )
00240 {
00241     access_sys_t *p_sys = p_access->p_sys;
00242     uint8_t i_tcid = i_slot + 1;
00243     int i_size;
00244     struct pollfd pfd[1];
00245 
00246     pfd[0].fd = p_sys->i_ca_handle;
00247     pfd[0].events = POLLIN;
00248     if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
00249     {
00250         msg_Err( p_access, "cannot poll from CAM device" );
00251         return VLC_EGENERIC;
00252     }
00253 
00254     if ( pi_size == NULL )
00255     {
00256         p_data = malloc( MAX_TPDU_SIZE );
00257     }
00258 
00259     for ( ; ; )
00260     {
00261         i_size = read( p_sys->i_ca_handle, p_data, MAX_TPDU_SIZE );
00262 
00263         if ( i_size >= 0 || errno != EINTR )
00264             break;
00265     }
00266 
00267     if ( i_size < 5 )
00268     {
00269         msg_Err( p_access, "cannot read from CAM device (%d:%s)", i_size,
00270                  strerror(errno) );
00271         return VLC_EGENERIC;
00272     }
00273 
00274     if ( p_data[1] != i_tcid )
00275     {
00276         msg_Err( p_access, "invalid read from CAM device (%d instead of %d)",
00277                  p_data[1], i_tcid );
00278         return VLC_EGENERIC;
00279     }
00280 
00281     *pi_tag = p_data[2];
00282     p_sys->pb_tc_has_data[i_slot] = (i_size >= 4
00283                                       && p_data[i_size - 4] == T_SB
00284                                       && p_data[i_size - 3] == 2
00285                                       && (p_data[i_size - 1] & DATA_INDICATOR))
00286                                         ?  VLC_TRUE : VLC_FALSE;
00287 
00288     Dump( VLC_FALSE, p_data, i_size );
00289 
00290     if ( pi_size == NULL )
00291         free( p_data );
00292     else
00293         *pi_size = i_size;
00294 
00295     return VLC_SUCCESS;
00296 }
00297 
00298 
00299 /*
00300  * Session layer
00301  */
00302 
00303 #define ST_SESSION_NUMBER           0x90
00304 #define ST_OPEN_SESSION_REQUEST     0x91
00305 #define ST_OPEN_SESSION_RESPONSE    0x92
00306 #define ST_CREATE_SESSION           0x93
00307 #define ST_CREATE_SESSION_RESPONSE  0x94
00308 #define ST_CLOSE_SESSION_REQUEST    0x95
00309 #define ST_CLOSE_SESSION_RESPONSE   0x96
00310 
00311 #define SS_OK             0x00
00312 #define SS_NOT_ALLOCATED  0xF0
00313 
00314 #define RI_RESOURCE_MANAGER            0x00010041
00315 #define RI_APPLICATION_INFORMATION     0x00020041
00316 #define RI_CONDITIONAL_ACCESS_SUPPORT  0x00030041
00317 #define RI_HOST_CONTROL                0x00200041
00318 #define RI_DATE_TIME                   0x00240041
00319 #define RI_MMI                         0x00400041
00320 
00321 static int ResourceIdToInt( uint8_t *p_data )
00322 {
00323     return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
00324             | ((int)p_data[2] << 8) | p_data[3];
00325 }
00326 
00327 /*****************************************************************************
00328  * SPDUSend
00329  *****************************************************************************/
00330 static int SPDUSend( access_t * p_access, int i_session_id,
00331                      uint8_t *p_data, int i_size )
00332 {
00333     access_sys_t *p_sys = p_access->p_sys;
00334     uint8_t *p_spdu = malloc( i_size + 4 );
00335     uint8_t *p = p_spdu;
00336     uint8_t i_tag;
00337     uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
00338 
00339     *p++ = ST_SESSION_NUMBER;
00340     *p++ = 0x02;
00341     *p++ = (i_session_id >> 8);
00342     *p++ = i_session_id & 0xff;
00343 
00344     memcpy( p, p_data, i_size );
00345 
00346     i_size += 4;
00347     p = p_spdu;
00348 
00349     while ( i_size > 0 )
00350     {
00351         if ( i_size > MAX_TPDU_DATA )
00352         {
00353             if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
00354                            MAX_TPDU_DATA ) != VLC_SUCCESS )
00355             {
00356                 msg_Err( p_access, "couldn't send TPDU on session %d",
00357                          i_session_id );
00358                 free( p_spdu );
00359                 return VLC_EGENERIC;
00360             }
00361             p += MAX_TPDU_DATA;
00362             i_size -= MAX_TPDU_DATA;
00363         }
00364         else
00365         {
00366             if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
00367                     != VLC_SUCCESS )
00368             {
00369                 msg_Err( p_access, "couldn't send TPDU on session %d",
00370                          i_session_id );
00371                 free( p_spdu );
00372                 return VLC_EGENERIC;
00373             }
00374             i_size = 0;
00375         }
00376 
00377         if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
00378                || i_tag != T_SB )
00379         {
00380             msg_Err( p_access, "couldn't recv TPDU on session %d",
00381                      i_session_id );
00382             free( p_spdu );
00383             return VLC_EGENERIC;
00384         }
00385     }
00386 
00387     free( p_spdu );
00388     return VLC_SUCCESS;
00389 }
00390 
00391 /*****************************************************************************
00392  * SessionOpen
00393  *****************************************************************************/
00394 static void SessionOpen( access_t * p_access, uint8_t i_slot,
00395                          uint8_t *p_spdu, int i_size )
00396 {
00397     access_sys_t *p_sys = p_access->p_sys;
00398     int i_session_id;
00399     int i_resource_id = ResourceIdToInt( &p_spdu[2] );
00400     uint8_t p_response[16];
00401     int i_status = SS_NOT_ALLOCATED;
00402     uint8_t i_tag;
00403 
00404     for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
00405     {
00406         if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
00407             break;
00408     }
00409     if ( i_session_id == MAX_SESSIONS )
00410     {
00411         msg_Err( p_access, "too many sessions !" );
00412         return;
00413     }
00414     p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
00415     p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
00416     p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
00417     p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
00418 
00419     if ( i_resource_id == RI_RESOURCE_MANAGER
00420           || i_resource_id == RI_APPLICATION_INFORMATION
00421           || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
00422           || i_resource_id == RI_DATE_TIME
00423           || i_resource_id == RI_MMI )
00424     {
00425         i_status = SS_OK;
00426     }
00427 
00428     p_response[0] = ST_OPEN_SESSION_RESPONSE;
00429     p_response[1] = 0x7;
00430     p_response[2] = i_status;
00431     p_response[3] = p_spdu[2];
00432     p_response[4] = p_spdu[3];
00433     p_response[5] = p_spdu[4];
00434     p_response[6] = p_spdu[5];
00435     p_response[7] = i_session_id >> 8;
00436     p_response[8] = i_session_id & 0xff;
00437 
00438     if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) !=
00439             VLC_SUCCESS )
00440     {
00441         msg_Err( p_access,
00442                  "SessionOpen: couldn't send TPDU on slot %d", i_slot );
00443         return;
00444     }
00445     if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
00446     {
00447         msg_Err( p_access,
00448                  "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
00449         return;
00450     }
00451 
00452     switch ( i_resource_id )
00453     {
00454     case RI_RESOURCE_MANAGER:
00455         ResourceManagerOpen( p_access, i_session_id ); break; 
00456     case RI_APPLICATION_INFORMATION:
00457         ApplicationInformationOpen( p_access, i_session_id ); break; 
00458     case RI_CONDITIONAL_ACCESS_SUPPORT:
00459         ConditionalAccessOpen( p_access, i_session_id ); break; 
00460     case RI_DATE_TIME:
00461         DateTimeOpen( p_access, i_session_id ); break; 
00462     case RI_MMI:
00463         MMIOpen( p_access, i_session_id ); break; 
00464 
00465     case RI_HOST_CONTROL:
00466     default:
00467         msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
00468         p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
00469     }
00470 }
00471 
00472 /*****************************************************************************
00473  * SessionClose
00474  *****************************************************************************/
00475 static void SessionClose( access_t * p_access, int i_session_id )
00476 {
00477     access_sys_t *p_sys = p_access->p_sys;
00478     uint8_t p_response[16];
00479     uint8_t i_tag;
00480     uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
00481 
00482     if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
00483         p_sys->p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
00484     p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
00485 
00486     p_response[0] = ST_CLOSE_SESSION_RESPONSE;
00487     p_response[1] = 0x3;
00488     p_response[2] = SS_OK;
00489     p_response[3] = i_session_id >> 8;
00490     p_response[4] = i_session_id & 0xff;
00491 
00492     if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
00493             VLC_SUCCESS )
00494     {
00495         msg_Err( p_access,
00496                  "SessionOpen: couldn't send TPDU on slot %d", i_slot );
00497         return;
00498     }
00499     if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
00500     {
00501         msg_Err( p_access,
00502                  "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
00503         return;
00504     }
00505 }
00506 
00507 /*****************************************************************************
00508  * SPDUHandle
00509  *****************************************************************************/
00510 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
00511                         uint8_t *p_spdu, int i_size )
00512 {
00513     access_sys_t *p_sys = p_access->p_sys;
00514     int i_session_id;
00515 
00516     switch ( p_spdu[0] )
00517     {
00518     case ST_SESSION_NUMBER:
00519         if ( i_size <= 4 )
00520             return;
00521         i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
00522         p_sys->p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
00523                                                        p_spdu + 4, i_size - 4 );
00524         break;
00525 
00526     case ST_OPEN_SESSION_REQUEST:
00527         if ( i_size != 6 || p_spdu[1] != 0x4 )
00528             return;
00529         SessionOpen( p_access, i_slot, p_spdu, i_size );
00530         break;
00531 
00532     case ST_CLOSE_SESSION_REQUEST:
00533         i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
00534         SessionClose( p_access, i_session_id );
00535         break;
00536 
00537     default:
00538         break;
00539     }
00540 }
00541 
00542 
00543 /*
00544  * Application layer
00545  */
00546 
00547 #define AOT_NONE                    0x000000
00548 #define AOT_PROFILE_ENQ             0x9F8010
00549 #define AOT_PROFILE                 0x9F8011
00550 #define AOT_PROFILE_CHANGE          0x9F8012
00551 #define AOT_APPLICATION_INFO_ENQ    0x9F8020
00552 #define AOT_APPLICATION_INFO        0x9F8021
00553 #define AOT_ENTER_MENU              0x9F8022
00554 #define AOT_CA_INFO_ENQ             0x9F8030
00555 #define AOT_CA_INFO                 0x9F8031
00556 #define AOT_CA_PMT                  0x9F8032
00557 #define AOT_CA_PMT_REPLY            0x9F8033
00558 #define AOT_TUNE                    0x9F8400
00559 #define AOT_REPLACE                 0x9F8401
00560 #define AOT_CLEAR_REPLACE           0x9F8402
00561 #define AOT_ASK_RELEASE             0x9F8403
00562 #define AOT_DATE_TIME_ENQ           0x9F8440
00563 #define AOT_DATE_TIME               0x9F8441
00564 #define AOT_CLOSE_MMI               0x9F8800
00565 #define AOT_DISPLAY_CONTROL         0x9F8801
00566 #define AOT_DISPLAY_REPLY           0x9F8802
00567 #define AOT_TEXT_LAST               0x9F8803
00568 #define AOT_TEXT_MORE               0x9F8804
00569 #define AOT_KEYPAD_CONTROL          0x9F8805
00570 #define AOT_KEYPRESS                0x9F8806
00571 #define AOT_ENQ                     0x9F8807
00572 #define AOT_ANSW                    0x9F8808
00573 #define AOT_MENU_LAST               0x9F8809
00574 #define AOT_MENU_MORE               0x9F880A
00575 #define AOT_MENU_ANSW               0x9F880B
00576 #define AOT_LIST_LAST               0x9F880C
00577 #define AOT_LIST_MORE               0x9F880D
00578 #define AOT_SUBTITLE_SEGMENT_LAST   0x9F880E
00579 #define AOT_SUBTITLE_SEGMENT_MORE   0x9F880F
00580 #define AOT_DISPLAY_MESSAGE         0x9F8810
00581 #define AOT_SCENE_END_MARK          0x9F8811
00582 #define AOT_SCENE_DONE              0x9F8812
00583 #define AOT_SCENE_CONTROL           0x9F8813
00584 #define AOT_SUBTITLE_DOWNLOAD_LAST  0x9F8814
00585 #define AOT_SUBTITLE_DOWNLOAD_MORE  0x9F8815
00586 #define AOT_FLUSH_DOWNLOAD          0x9F8816
00587 #define AOT_DOWNLOAD_REPLY          0x9F8817
00588 #define AOT_COMMS_CMD               0x9F8C00
00589 #define AOT_CONNECTION_DESCRIPTOR   0x9F8C01
00590 #define AOT_COMMS_REPLY             0x9F8C02
00591 #define AOT_COMMS_SEND_LAST         0x9F8C03
00592 #define AOT_COMMS_SEND_MORE         0x9F8C04
00593 #define AOT_COMMS_RCV_LAST          0x9F8C05
00594 #define AOT_COMMS_RCV_MORE          0x9F8C06
00595 
00596 /*****************************************************************************
00597  * APDUGetTag
00598  *****************************************************************************/
00599 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
00600 {
00601     if ( i_size >= 3 )
00602     {
00603         int i, t = 0;
00604         for ( i = 0; i < 3; i++ )
00605             t = (t << 8) | *p_apdu++;
00606         return t;
00607     }
00608 
00609     return AOT_NONE;
00610 }
00611 
00612 /*****************************************************************************
00613  * APDUGetLength
00614  *****************************************************************************/
00615 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
00616 {
00617     return GetLength( &p_apdu[3], pi_size );
00618 }
00619 
00620 /*****************************************************************************
00621  * APDUSend
00622  *****************************************************************************/
00623 static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
00624                      uint8_t *p_data, int i_size )
00625 {
00626     uint8_t *p_apdu = malloc( i_size + 12 );
00627     uint8_t *p = p_apdu;
00628     int i_ret;
00629 
00630     *p++ = (i_tag >> 16);
00631     *p++ = (i_tag >> 8) & 0xff;
00632     *p++ = i_tag & 0xff;
00633     p = SetLength( p, i_size );
00634     if ( i_size )
00635         memcpy( p, p_data, i_size );
00636 
00637     i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
00638     free( p_apdu );
00639     return i_ret;
00640 }
00641 
00642 /*
00643  * Resource Manager
00644  */
00645 
00646 /*****************************************************************************
00647  * ResourceManagerHandle
00648  *****************************************************************************/
00649 static void ResourceManagerHandle( access_t * p_access, int i_session_id,
00650                                    uint8_t *p_apdu, int i_size )
00651 {
00652     int i_tag = APDUGetTag( p_apdu, i_size );
00653 
00654     switch ( i_tag )
00655     {
00656     case AOT_PROFILE_ENQ:
00657     {
00658         int resources[] = { htonl(RI_RESOURCE_MANAGER),
00659                             htonl(RI_APPLICATION_INFORMATION),
00660                             htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
00661                             htonl(RI_DATE_TIME),
00662                             htonl(RI_MMI)
00663                           };
00664         APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
00665                   sizeof(resources) );
00666         break;
00667     }
00668     case AOT_PROFILE:
00669         APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
00670         break;
00671 
00672     default:
00673         msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
00674                  i_tag );
00675     }
00676 }
00677 
00678 /*****************************************************************************
00679  * ResourceManagerOpen
00680  *****************************************************************************/
00681 static void ResourceManagerOpen( access_t * p_access, int i_session_id )
00682 {
00683     access_sys_t *p_sys = p_access->p_sys;
00684 
00685     msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
00686 
00687     p_sys->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
00688 
00689     APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
00690 }
00691 
00692 /*
00693  * Application Information
00694  */
00695 
00696 /*****************************************************************************
00697  * ApplicationInformationHandle
00698  *****************************************************************************/
00699 static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
00700                                           uint8_t *p_apdu, int i_size )
00701 {
00702     int i_tag = APDUGetTag( p_apdu, i_size );
00703 
00704     switch ( i_tag )
00705     {
00706     case AOT_APPLICATION_INFO:
00707     {
00708         int i_type, i_manufacturer, i_code;
00709         int l = 0;
00710         uint8_t *d = APDUGetLength( p_apdu, &l );
00711 
00712         if ( l < 4 ) break;
00713         p_apdu[l + 4] = '\0';
00714 
00715         i_type = *d++;
00716         i_manufacturer = ((int)d[0] << 8) | d[1];
00717         d += 2;
00718         i_code = ((int)d[0] << 8) | d[1];
00719         d += 2;
00720         d = GetLength( d, &l );
00721         d[l] = '\0';
00722         msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
00723                   d, i_type, i_manufacturer, i_code );
00724         break;
00725     }
00726     default:
00727         msg_Err( p_access,
00728                  "unexpected tag in ApplicationInformationHandle (0x%x)",
00729                  i_tag );
00730     }
00731 }
00732 
00733 /*****************************************************************************
00734  * ApplicationInformationOpen
00735  *****************************************************************************/
00736 static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
00737 {
00738     access_sys_t *p_sys = p_access->p_sys;
00739 
00740     msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
00741 
00742     p_sys->p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
00743 
00744     APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
00745 }
00746 
00747 /*
00748  * Conditional Access
00749  */
00750 
00751 #define MAX_CASYSTEM_IDS 16
00752 
00753 typedef struct
00754 {
00755     uint16_t pi_system_ids[MAX_CASYSTEM_IDS + 1];
00756 } system_ids_t;
00757 
00758 static vlc_bool_t CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
00759 {
00760     int i = 0;
00761     while ( p_ids->pi_system_ids[i] )
00762     {
00763         if ( p_ids->pi_system_ids[i] == i_id )
00764             return VLC_TRUE;
00765         i++;
00766     }
00767 
00768     return VLC_FALSE;
00769 }
00770 
00771 /*****************************************************************************
00772  * CAPMTNeedsDescrambling
00773  *****************************************************************************/
00774 static vlc_bool_t CAPMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
00775 {
00776     dvbpsi_descriptor_t *p_dr;
00777     dvbpsi_pmt_es_t *p_es;
00778 
00779     for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
00780     {
00781         if( p_dr->i_tag == 0x9 )
00782         {
00783             return VLC_TRUE;
00784         }
00785     }
00786     
00787     for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
00788     {
00789         for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
00790              p_dr = p_dr->p_next )
00791         {
00792             if( p_dr->i_tag == 0x9 )
00793             {
00794                 return VLC_TRUE;
00795             }
00796         }
00797     }
00798 
00799     return VLC_FALSE;
00800 }
00801 
00802 /*****************************************************************************
00803  * CAPMTBuild
00804  *****************************************************************************/
00805 static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr )
00806 {
00807     int i_cad_size = 0;
00808 
00809     while ( p_dr != NULL )
00810     {
00811         if( p_dr->i_tag == 0x9 )
00812         {
00813             uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
00814                                     | p_dr->p_data[1];
00815             if ( CheckSystemID( p_ids, i_sysid ) )
00816                 i_cad_size += p_dr->i_length + 2;
00817         }
00818         p_dr = p_dr->p_next;
00819     }
00820 
00821     return i_cad_size;
00822 }
00823 
00824 static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt,
00825                              uint16_t i_program_number, uint8_t i_version,
00826                              int i_size, dvbpsi_descriptor_t *p_dr,
00827                              uint8_t i_cmd )
00828 {
00829     uint8_t *p_data;
00830 
00831     if ( i_size )
00832         p_data = malloc( 7 + i_size );
00833     else
00834         p_data = malloc( 6 );
00835 
00836     p_data[0] = i_list_mgt;
00837     p_data[1] = i_program_number >> 8;
00838     p_data[2] = i_program_number & 0xff;
00839     p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
00840 
00841     if ( i_size )
00842     {
00843         int i;
00844 
00845         p_data[4] = (i_size + 1) >> 8;
00846         p_data[5] = (i_size + 1) & 0xff;
00847         p_data[6] = i_cmd;
00848         i = 7;
00849 
00850         while ( p_dr != NULL )
00851         {
00852             if( p_dr->i_tag == 0x9 )
00853             {
00854                 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
00855                                     | p_dr->p_data[1];
00856                 if ( CheckSystemID( p_ids, i_sysid ) )
00857                 {
00858                     p_data[i] = 0x9;
00859                     p_data[i + 1] = p_dr->i_length;
00860                     memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
00861                     i += p_dr->i_length + 2;
00862                 }
00863             }
00864             p_dr = p_dr->p_next;
00865         }
00866     }
00867     else
00868     {
00869         p_data[4] = 0;
00870         p_data[5] = 0;
00871     }
00872 
00873     return p_data;
00874 }
00875 
00876 static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt,
00877                          int i_capmt_size, uint8_t i_type, uint16_t i_pid,
00878                          int i_size, dvbpsi_descriptor_t *p_dr,
00879                          uint8_t i_cmd )
00880 {
00881     uint8_t *p_data;
00882     int i;
00883     
00884     if ( i_size )
00885         p_data = realloc( p_capmt, i_capmt_size + 6 + i_size );
00886     else
00887         p_data = realloc( p_capmt, i_capmt_size + 5 );
00888 
00889     i = i_capmt_size;
00890 
00891     p_data[i] = i_type;
00892     p_data[i + 1] = i_pid >> 8;
00893     p_data[i + 2] = i_pid & 0xff;
00894 
00895     if ( i_size )
00896     {
00897         p_data[i + 3] = (i_size + 1) >> 8;
00898         p_data[i + 4] = (i_size + 1) & 0xff;
00899         p_data[i + 5] = i_cmd;
00900         i += 6;
00901 
00902         while ( p_dr != NULL )
00903         {
00904             if( p_dr->i_tag == 0x9 )
00905             {
00906                 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
00907                                     | p_dr->p_data[1];
00908                 if ( CheckSystemID( p_ids, i_sysid ) )
00909                 {
00910                     p_data[i] = 0x9;
00911                     p_data[i + 1] = p_dr->i_length;
00912                     memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
00913                     i += p_dr->i_length + 2;
00914                 }
00915             }
00916             p_dr = p_dr->p_next;
00917         }
00918     }
00919     else
00920     {
00921         p_data[i + 3] = 0;
00922         p_data[i + 4] = 0;
00923     }
00924 
00925     return p_data;
00926 }
00927 
00928 static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id,
00929                             dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt,
00930                             uint8_t i_cmd, int *pi_capmt_size )
00931 {
00932     access_sys_t *p_sys = p_access->p_sys;
00933     system_ids_t *p_ids =
00934         (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
00935     dvbpsi_pmt_es_t *p_es;
00936     int i_cad_size, i_cad_program_size;
00937     uint8_t *p_capmt;
00938 
00939     i_cad_size = i_cad_program_size =
00940             GetCADSize( p_ids, p_pmt->p_first_descriptor );
00941     for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
00942     {
00943         i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor );
00944     }
00945 
00946     if ( !i_cad_size )
00947     {
00948         msg_Warn( p_access,
00949                   "no compatible scrambling system for SID %d on session %d",
00950                   p_pmt->i_program_number, i_session_id );
00951         *pi_capmt_size = 0;
00952         return NULL;
00953     }
00954 
00955     p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number,
00956                            p_pmt->i_version, i_cad_program_size,
00957                            p_pmt->p_first_descriptor, i_cmd );
00958 
00959     if ( i_cad_program_size )
00960         *pi_capmt_size = 7 + i_cad_program_size;
00961     else
00962         *pi_capmt_size = 6;
00963 
00964     for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
00965     {
00966         i_cad_size = GetCADSize( p_ids, p_es->p_first_descriptor );
00967 
00968         if ( i_cad_size || i_cad_program_size )
00969         {
00970             p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type,
00971                                p_es->i_pid, i_cad_size,
00972                                p_es->p_first_descriptor, i_cmd );
00973             if ( i_cad_size )
00974                 *pi_capmt_size += 6 + i_cad_size;
00975             else
00976                 *pi_capmt_size += 5;
00977         }
00978     }
00979 
00980     return p_capmt;
00981 }
00982 
00983 /*****************************************************************************
00984  * CAPMTFirst
00985  *****************************************************************************/
00986 static void CAPMTFirst( access_t * p_access, int i_session_id,
00987                         dvbpsi_pmt_t *p_pmt )
00988 {
00989     uint8_t *p_capmt;
00990     int i_capmt_size;
00991 
00992     msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d",
00993              p_pmt->i_program_number, i_session_id );
00994 
00995     p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
00996                           0x3 /* only */, 0x1 /* ok_descrambling */,
00997                           &i_capmt_size );
00998 
00999     if ( i_capmt_size )
01000         APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
01001 }
01002 
01003 /*****************************************************************************
01004  * CAPMTAdd
01005  *****************************************************************************/
01006 static void CAPMTAdd( access_t * p_access, int i_session_id,
01007                       dvbpsi_pmt_t *p_pmt )
01008 {
01009     uint8_t *p_capmt;
01010     int i_capmt_size;
01011 
01012     msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
01013              p_pmt->i_program_number, i_session_id );
01014 
01015     p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
01016                           0x4 /* add */, 0x1 /* ok_descrambling */,
01017                           &i_capmt_size );
01018 
01019     if ( i_capmt_size )
01020         APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
01021 }
01022 
01023 /*****************************************************************************
01024  * CAPMTUpdate
01025  *****************************************************************************/
01026 static void CAPMTUpdate( access_t * p_access, int i_session_id,
01027                          dvbpsi_pmt_t *p_pmt )
01028 {
01029     uint8_t *p_capmt;
01030     int i_capmt_size;
01031 
01032     msg_Dbg( p_access, "updating CAPMT for SID %d on session %d",
01033              p_pmt->i_program_number, i_session_id );
01034 
01035     p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
01036                           0x5 /* update */, 0x1 /* ok_descrambling */,
01037                           &i_capmt_size );
01038 
01039     if ( i_capmt_size )
01040         APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
01041 }
01042 
01043 /*****************************************************************************
01044  * CAPMTDelete
01045  *****************************************************************************/
01046 static void CAPMTDelete( access_t * p_access, int i_session_id,
01047                          dvbpsi_pmt_t *p_pmt )
01048 {
01049     uint8_t *p_capmt;
01050     int i_capmt_size;
01051 
01052     msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d",
01053              p_pmt->i_program_number, i_session_id );
01054 
01055     p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
01056                           0x5 /* update */, 0x4 /* not selected */,
01057                           &i_capmt_size );
01058 
01059     if ( i_capmt_size )
01060         APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
01061 }
01062 
01063 /*****************************************************************************
01064  * ConditionalAccessHandle
01065  *****************************************************************************/
01066 static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
01067                                      uint8_t *p_apdu, int i_size )
01068 {
01069     access_sys_t *p_sys = p_access->p_sys;
01070     system_ids_t *p_ids =
01071         (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
01072     int i_tag = APDUGetTag( p_apdu, i_size );
01073 
01074     switch ( i_tag )
01075     {
01076     case AOT_CA_INFO:
01077     {
01078         vlc_bool_t b_inited = VLC_FALSE;
01079         int i;
01080         int l = 0;
01081         uint8_t *d = APDUGetLength( p_apdu, &l );
01082         msg_Dbg( p_access, "CA system IDs supported by the application :" );
01083 
01084         for ( i = 0; i < l / 2; i++ )
01085         {
01086             p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
01087             d += 2;
01088             msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] );
01089         }
01090         p_ids->pi_system_ids[i] = 0;
01091 
01092         for ( i = 0; i < MAX_PROGRAMS; i++ )
01093         {
01094             if ( p_sys->pp_selected_programs[i] != NULL )
01095             {
01096                 if ( b_inited )
01097                     CAPMTAdd( p_access, i_session_id,
01098                               p_sys->pp_selected_programs[i] );
01099                 else
01100                     CAPMTFirst( p_access, i_session_id,
01101                                 p_sys->pp_selected_programs[i] );
01102                 b_inited = VLC_TRUE;
01103             }
01104         }
01105         break;
01106     }
01107 
01108     default:
01109         msg_Err( p_access,
01110                  "unexpected tag in ConditionalAccessHandle (0x%x)",
01111                  i_tag );
01112     }
01113 }
01114 
01115 /*****************************************************************************
01116  * ConditionalAccessOpen
01117  *****************************************************************************/
01118 static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
01119 {
01120     access_sys_t *p_sys = p_access->p_sys;
01121 
01122     msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
01123 
01124     p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
01125     p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(system_ids_t));
01126     memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0,
01127             sizeof(system_ids_t) );
01128 
01129     APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
01130 }
01131 
01132 /*
01133  * Date Time
01134  */
01135 
01136 typedef struct
01137 {
01138     int i_interval;
01139     mtime_t i_last;
01140 } date_time_t;
01141 
01142 /*****************************************************************************
01143  * DateTimeSend
01144  *****************************************************************************/
01145 static void DateTimeSend( access_t * p_access, int i_session_id )
01146 {
01147     access_sys_t *p_sys = p_access->p_sys;
01148     date_time_t *p_date =
01149         (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
01150 
01151     time_t t = time(NULL);
01152     struct tm tm_gmt;
01153     struct tm tm_loc;
01154 
01155     if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
01156     {
01157         int Y = tm_gmt.tm_year;
01158         int M = tm_gmt.tm_mon + 1;
01159         int D = tm_gmt.tm_mday;
01160         int L = (M == 1 || M == 2) ? 1 : 0;
01161         int MJD = 14956 + D + (int)((Y - L) * 365.25)
01162                     + (int)((M + 1 + L * 12) * 30.6001);
01163         uint8_t p_response[7];
01164 
01165 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
01166 
01167         p_response[0] = htons(MJD) >> 8;
01168         p_response[1] = htons(MJD) & 0xff;
01169         p_response[2] = DEC2BCD(tm_gmt.tm_hour);
01170         p_response[3] = DEC2BCD(tm_gmt.tm_min);
01171         p_response[4] = DEC2BCD(tm_gmt.tm_sec);
01172         p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
01173         p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
01174 
01175         APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
01176 
01177         p_date->i_last = mdate();
01178     }
01179 }
01180 
01181 /*****************************************************************************
01182  * DateTimeHandle
01183  *****************************************************************************/
01184 static void DateTimeHandle( access_t * p_access, int i_session_id,
01185                             uint8_t *p_apdu, int i_size )
01186 {
01187     access_sys_t *p_sys = p_access->p_sys;
01188     date_time_t *p_date =
01189         (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
01190 
01191     int i_tag = APDUGetTag( p_apdu, i_size );
01192 
01193     switch ( i_tag )
01194     {
01195     case AOT_DATE_TIME_ENQ:
01196     {
01197         int l;
01198         const uint8_t *d = APDUGetLength( p_apdu, &l );
01199 
01200         if ( l > 0 )
01201         {
01202             p_date->i_interval = *d;
01203             msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
01204                      p_date->i_interval );
01205         }
01206         else
01207             p_date->i_interval = 0;
01208 
01209         DateTimeSend( p_access, i_session_id );
01210         break;
01211     }
01212     default:
01213         msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
01214     }
01215 }
01216 
01217 /*****************************************************************************
01218  * DateTimeManage
01219  *****************************************************************************/
01220 static void DateTimeManage( access_t * p_access, int i_session_id )
01221 {
01222     access_sys_t *p_sys = p_access->p_sys;
01223     date_time_t *p_date =
01224         (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
01225 
01226     if ( p_date->i_interval
01227           && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
01228     {
01229         DateTimeSend( p_access, i_session_id );
01230     }
01231 }
01232 
01233 /*****************************************************************************
01234  * DateTimeOpen
01235  *****************************************************************************/
01236 static void DateTimeOpen( access_t * p_access, int i_session_id )
01237 {
01238     access_sys_t *p_sys = p_access->p_sys;
01239 
01240     msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
01241 
01242     p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
01243     p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
01244     p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t));
01245     memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
01246 
01247     DateTimeSend( p_access, i_session_id );
01248 }
01249 
01250 /*
01251  * MMI
01252  */
01253 
01254 /*****************************************************************************
01255  * MMIHandle
01256  *****************************************************************************/
01257 static void MMIHandle( access_t * p_access, int i_session_id,
01258                             uint8_t *p_apdu, int i_size )
01259 {
01260     int i_tag = APDUGetTag( p_apdu, i_size );
01261 
01262     switch ( i_tag )
01263     {
01264     default:
01265         msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
01266     }
01267 }
01268 
01269 /*****************************************************************************
01270  * MMIOpen
01271  *****************************************************************************/
01272 static void MMIOpen( access_t * p_access, int i_session_id )
01273 {
01274     access_sys_t *p_sys = p_access->p_sys;
01275 
01276     msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
01277 
01278     p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
01279 }
01280 
01281 
01282 /*
01283  * Hardware handling
01284  */
01285 
01286 /*****************************************************************************
01287  * InitSlot: Open the transport layer
01288  *****************************************************************************/
01289 #define MAX_TC_RETRIES 20
01290 
01291 static int InitSlot( access_t * p_access, int i_slot )
01292 {
01293     access_sys_t *p_sys = p_access->p_sys;
01294     int i;
01295 
01296     if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
01297             != VLC_SUCCESS )
01298     {
01299         msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
01300                  i_slot );
01301         return VLC_EGENERIC;
01302     }
01303 
01304     /* This is out of the spec */
01305     for ( i = 0; i < MAX_TC_RETRIES; i++ )
01306     {
01307         uint8_t i_tag;
01308         if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
01309               && i_tag == T_CTC_REPLY )
01310         {
01311             p_sys->pb_active_slot[i_slot] = VLC_TRUE;
01312             break;
01313         }
01314 
01315         if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
01316                 != VLC_SUCCESS )
01317         {
01318             msg_Err( p_access,
01319                      "en50221_Init: couldn't send TPDU on slot %d",
01320                      i_slot );
01321             continue;
01322         }
01323     }
01324     if ( p_sys->pb_active_slot[i_slot] )
01325     {
01326         p_sys->i_ca_timeout = 100000;
01327         return VLC_SUCCESS;
01328     }
01329 
01330     return VLC_EGENERIC;
01331 }
01332 
01333 
01334 /*
01335  * External entry points
01336  */
01337 
01338 /*****************************************************************************
01339  * en50221_Poll : Poll the CAM for TPDUs
01340  *****************************************************************************/
01341 int E_(en50221_Poll)( access_t * p_access )
01342 {
01343     access_sys_t *p_sys = p_access->p_sys;
01344     int i_slot;
01345     int i_session_id;
01346 
01347     for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
01348     {
01349         uint8_t i_tag;
01350 
01351         if ( !p_sys->pb_active_slot[i_slot] )
01352         {
01353             ca_slot_info_t sinfo;
01354             sinfo.num = i_slot;
01355             if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
01356             {
01357                 msg_Err( p_access, "en50221_Poll: couldn't get info on slot %d",
01358                          i_slot );
01359                 continue;
01360             }
01361 
01362             if ( sinfo.flags & CA_CI_MODULE_READY )
01363             {
01364                 msg_Dbg( p_access, "en50221_Poll: slot %d is active",
01365                          i_slot );
01366                 p_sys->pb_active_slot[i_slot] = VLC_TRUE;
01367             }
01368             else
01369                 continue;
01370 
01371             InitSlot( p_access, i_slot );
01372         }
01373 
01374         if ( !p_sys->pb_tc_has_data[i_slot] )
01375         {
01376             if ( TPDUSend( p_access, i_slot, T_DATA_LAST, NULL, 0 ) !=
01377                     VLC_SUCCESS )
01378             {
01379                 msg_Err( p_access,
01380                          "en50221_Poll: couldn't send TPDU on slot %d",
01381                          i_slot );
01382                 continue;
01383             }
01384             if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) !=
01385                     VLC_SUCCESS )
01386             {
01387                 msg_Err( p_access,
01388                          "en50221_Poll: couldn't recv TPDU on slot %d",
01389                          i_slot );
01390                 continue;
01391             }
01392         }
01393 
01394         while ( p_sys->pb_tc_has_data[i_slot] )
01395         {
01396             uint8_t p_tpdu[MAX_TPDU_SIZE];
01397             int i_size, i_session_size;
01398             uint8_t *p_session;
01399 
01400             if ( TPDUSend( p_access, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
01401             {
01402                 msg_Err( p_access,
01403                          "en50221_Poll: couldn't send TPDU on slot %d",
01404                          i_slot );
01405                 continue;
01406             }
01407             if ( TPDURecv( p_access, i_slot, &i_tag, p_tpdu, &i_size ) !=
01408                     VLC_SUCCESS )
01409             {
01410                 msg_Err( p_access,
01411                          "en50221_Poll: couldn't recv TPDU on slot %d",
01412                          i_slot );
01413                 continue;
01414             }
01415 
01416             p_session = GetLength( &p_tpdu[3], &i_session_size );
01417             if ( i_session_size <= 1 )
01418                 continue;
01419 
01420             p_session++;
01421             i_session_size--;
01422 
01423             if ( i_tag != T_DATA_LAST )
01424             {
01425                 msg_Err( p_access,
01426                          "en50221_Poll: fragmented TPDU not supported" );
01427                 break;
01428             }
01429 
01430             SPDUHandle( p_access, i_slot, p_session, i_session_size );
01431         }
01432     }
01433 
01434     for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
01435     {
01436         if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
01437               && p_sys->p_sessions[i_session_id - 1].pf_manage )
01438         {
01439             p_sys->p_sessions[i_session_id - 1].pf_manage( p_access,
01440                                                            i_session_id );
01441         }
01442     }
01443 
01444     return VLC_SUCCESS;
01445 }
01446 
01447 
01448 /*****************************************************************************
01449  * en50221_SetCAPMT :
01450  *****************************************************************************/
01451 int E_(en50221_SetCAPMT)( access_t * p_access, dvbpsi_pmt_t *p_pmt )
01452 {
01453     access_sys_t *p_sys = p_access->p_sys;
01454     int i, i_session_id;
01455     vlc_bool_t b_update = VLC_FALSE;
01456     vlc_bool_t b_needs_descrambling = CAPMTNeedsDescrambling( p_pmt );
01457 
01458     for ( i = 0; i < MAX_PROGRAMS; i++ )
01459     {
01460         if ( p_sys->pp_selected_programs[i] != NULL
01461               && p_sys->pp_selected_programs[i]->i_program_number
01462                   == p_pmt->i_program_number )
01463         {
01464             b_update = VLC_TRUE;
01465 
01466             if ( !b_needs_descrambling )
01467             {
01468                 dvbpsi_DeletePMT( p_pmt );
01469                 p_pmt = p_sys->pp_selected_programs[i];
01470                 p_sys->pp_selected_programs[i] = NULL;
01471             }
01472             else
01473             {
01474                 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
01475                 p_sys->pp_selected_programs[i] = p_pmt;
01476             }
01477 
01478             break;
01479         }
01480     }
01481 
01482     if ( !b_update && b_needs_descrambling )
01483     {
01484         for ( i = 0; i < MAX_PROGRAMS; i++ )
01485         {
01486             if ( p_sys->pp_selected_programs[i] == NULL )
01487             {
01488                 p_sys->pp_selected_programs[i] = p_pmt;
01489                 break;
01490             }
01491         }
01492     }
01493 
01494     if ( b_update || b_needs_descrambling )
01495     {
01496         for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
01497         {
01498             if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
01499                     == RI_CONDITIONAL_ACCESS_SUPPORT )
01500             {
01501                 if ( b_update && b_needs_descrambling )
01502                     CAPMTUpdate( p_access, i_session_id, p_pmt );
01503                 else if ( b_update )
01504                     CAPMTDelete( p_access, i_session_id, p_pmt );
01505                 else
01506                     CAPMTAdd( p_access, i_session_id, p_pmt );
01507             }
01508         }
01509     }
01510 
01511     if ( !b_needs_descrambling )
01512     {
01513         dvbpsi_DeletePMT( p_pmt );
01514     }
01515 
01516     return VLC_SUCCESS;
01517 }
01518 
01519 /*****************************************************************************
01520  * en50221_End :
01521  *****************************************************************************/
01522 void E_(en50221_End)( access_t * p_access )
01523 {
01524     access_sys_t *p_sys = p_access->p_sys;
01525     int i;
01526 
01527     for ( i = 0; i < MAX_PROGRAMS; i++ )
01528     {
01529         if ( p_sys->pp_selected_programs[i] != NULL )
01530         {
01531             dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
01532         }
01533     }
01534 
01535     /* TODO */
01536 }

Generated on Tue Dec 20 10:14:24 2005 for vlc-0.8.4a by  doxygen 1.4.2