en50221.c
上传用户:riyaled888
上传日期:2009-03-27
资源大小:7338k
文件大小:34k
源码类别:

多媒体

开发平台:

MultiPlatform

  1. /*****************************************************************************
  2.  * en50221.c : implementation of the transport, session and applications
  3.  * layers of EN 50 221
  4.  *****************************************************************************
  5.  * Copyright (C) 2004 VideoLAN
  6.  *
  7.  * Authors: Christophe Massiot <massiot@via.ecp.fr>
  8.  * Based on code from libdvbci Copyright (C) 2000 Klaus Schmidinger
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA    02111, USA.
  23.  *****************************************************************************/
  24. #include <vlc/vlc.h>
  25. #include <vlc/input.h>
  26. #include <sys/ioctl.h>
  27. #include <errno.h>
  28. #include <sys/types.h>
  29. #include <sys/stat.h>
  30. #include <fcntl.h>
  31. #include <time.h>
  32. #include <unistd.h>
  33. #include <sys/stat.h>
  34. #include <sys/poll.h>
  35. /* DVB Card Drivers */
  36. #include <linux/dvb/version.h>
  37. #include <linux/dvb/dmx.h>
  38. #include <linux/dvb/frontend.h>
  39. #include <linux/dvb/ca.h>
  40. #include "dvb.h"
  41. #undef DEBUG_TPDU
  42. static void ResourceManagerOpen( access_t * p_access, int i_session_id );
  43. static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
  44. static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
  45. static void DateTimeOpen( access_t * p_access, int i_session_id );
  46. static void MMIOpen( access_t * p_access, int i_session_id );
  47. /*****************************************************************************
  48.  * Utility functions
  49.  *****************************************************************************/
  50. #define SIZE_INDICATOR 0x80
  51. static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
  52. {
  53.     *pi_length = *p_data++;
  54.     if ( (*pi_length & SIZE_INDICATOR) != 0 )
  55.     {
  56.         int l = *pi_length & ~SIZE_INDICATOR;
  57.         int i;
  58.         *pi_length = 0;
  59.         for ( i = 0; i < l; i++ )
  60.             *pi_length = (*pi_length << 8) | *p_data++;
  61.     }
  62.     return p_data;
  63. }
  64. static uint8_t *SetLength( uint8_t *p_data, int i_length )
  65. {
  66.     uint8_t *p = p_data;
  67.     if ( i_length < 128 )
  68.     {
  69.         *p++ = i_length;
  70.     }
  71.     else if ( i_length < 256 )
  72.     {
  73.         *p++ = SIZE_INDICATOR | 0x1;
  74.         *p++ = i_length;
  75.     }
  76.     else if ( i_length < 65536 )
  77.     {
  78.         *p++ = SIZE_INDICATOR | 0x2;
  79.         *p++ = i_length >> 8;
  80.         *p++ = i_length & 0xff;
  81.     }
  82.     else if ( i_length < 16777216 )
  83.     {
  84.         *p++ = SIZE_INDICATOR | 0x3;
  85.         *p++ = i_length >> 16;
  86.         *p++ = (i_length >> 8) & 0xff;
  87.         *p++ = i_length & 0xff;
  88.     }
  89.     else
  90.     {
  91.         *p++ = SIZE_INDICATOR | 0x4;
  92.         *p++ = i_length >> 24;
  93.         *p++ = (i_length >> 16) & 0xff;
  94.         *p++ = (i_length >> 8) & 0xff;
  95.         *p++ = i_length & 0xff;
  96.     }
  97.     return p;
  98. }
  99. /*
  100.  * Transport layer
  101.  */
  102. #define MAX_TPDU_SIZE  2048
  103. #define MAX_TPDU_DATA  (MAX_TPDU_SIZE - 4)
  104. #define DATA_INDICATOR 0x80
  105. #define T_SB           0x80
  106. #define T_RCV          0x81
  107. #define T_CREATE_TC    0x82
  108. #define T_CTC_REPLY    0x83
  109. #define T_DELETE_TC    0x84
  110. #define T_DTC_REPLY    0x85
  111. #define T_REQUEST_TC   0x86
  112. #define T_NEW_TC       0x87
  113. #define T_TC_ERROR     0x88
  114. #define T_DATA_LAST    0xA0
  115. #define T_DATA_MORE    0xA1
  116. static void Dump( vlc_bool_t b_outgoing, uint8_t *p_data, int i_size )
  117. {
  118. #ifdef DEBUG_TPDU
  119.     int i;
  120. #define MAX_DUMP 256
  121.     fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
  122.     for ( i = 0; i < i_size && i < MAX_DUMP; i++)
  123.         fprintf(stderr, "%02X ", p_data[i]);
  124.     fprintf(stderr, "%sn", i_size >= MAX_DUMP ? "..." : "");
  125. #endif
  126. }
  127. /*****************************************************************************
  128.  * TPDUSend
  129.  *****************************************************************************/
  130. static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
  131.                      const uint8_t *p_content, int i_length )
  132. {
  133.     access_sys_t *p_sys = p_access->p_sys;
  134.     uint8_t i_tcid = i_slot + 1;
  135.     uint8_t p_data[MAX_TPDU_SIZE];
  136.     int i_size;
  137.     i_size = 0;
  138.     p_data[0] = i_slot;
  139.     p_data[1] = i_tcid;
  140.     p_data[2] = i_tag;
  141.     switch ( i_tag )
  142.     {
  143.     case T_RCV:
  144.     case T_CREATE_TC:
  145.     case T_CTC_REPLY:
  146.     case T_DELETE_TC:
  147.     case T_DTC_REPLY:
  148.     case T_REQUEST_TC:
  149.         p_data[3] = 1; /* length */
  150.         p_data[4] = i_tcid;
  151.         i_size = 5;
  152.         break;
  153.     case T_NEW_TC:
  154.     case T_TC_ERROR:
  155.         p_data[3] = 2; /* length */
  156.         p_data[4] = i_tcid;
  157.         p_data[5] = p_content[0];
  158.         i_size = 6;
  159.         break;
  160.     case T_DATA_LAST:
  161.     case T_DATA_MORE:
  162.     {
  163.         /* i_length <= MAX_TPDU_DATA */
  164.         uint8_t *p = p_data + 3;
  165.         p = SetLength( p, i_length + 1 );
  166.         *p++ = i_tcid;
  167.         if ( i_length )
  168.             memcpy( p, p_content, i_length );
  169.             i_size = i_length + (p - p_data);
  170.         }
  171.         break;
  172.     default:
  173.         break;
  174.     }
  175.     Dump( VLC_TRUE, p_data, i_size );
  176.     if ( write( p_sys->i_ca_handle, p_data, i_size ) != i_size )
  177.     {
  178.         msg_Err( p_access, "cannot write to CAM device (%s)",
  179.                  strerror(errno) );
  180.         return VLC_EGENERIC;
  181.     }
  182.     return VLC_SUCCESS;
  183. }
  184. /*****************************************************************************
  185.  * TPDURecv
  186.  *****************************************************************************/
  187. #define CAM_READ_TIMEOUT  3500 // ms
  188. static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
  189.                      uint8_t *p_data, int *pi_size )
  190. {
  191.     access_sys_t *p_sys = p_access->p_sys;
  192.     uint8_t i_tcid = i_slot + 1;
  193.     int i_size;
  194.     struct pollfd pfd[1];
  195.     pfd[0].fd = p_sys->i_ca_handle;
  196.     pfd[0].events = POLLIN;
  197.     if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
  198.     {
  199.         msg_Err( p_access, "cannot poll from CAM device" );
  200.         return VLC_EGENERIC;
  201.     }
  202.     if ( pi_size == NULL )
  203.     {
  204.         p_data = malloc( MAX_TPDU_SIZE );
  205.     }
  206.     for ( ; ; )
  207.     {
  208.         i_size = read( p_sys->i_ca_handle, p_data, MAX_TPDU_SIZE );
  209.         if ( i_size >= 0 || errno != EINTR )
  210.             break;
  211.     }
  212.     if ( i_size < 5 )
  213.     {
  214.         msg_Err( p_access, "cannot read from CAM device (%d:%s)", i_size,
  215.                  strerror(errno) );
  216.         return VLC_EGENERIC;
  217.     }
  218.     if ( p_data[1] != i_tcid )
  219.     {
  220.         msg_Err( p_access, "invalid read from CAM device (%d instead of %d)",
  221.                  p_data[1], i_tcid );
  222.         return VLC_EGENERIC;
  223.     }
  224.     *pi_tag = p_data[2];
  225.     p_sys->pb_tc_has_data[i_slot] = (i_size >= 4
  226.                                       && p_data[i_size - 4] == T_SB
  227.                                       && p_data[i_size - 3] == 2
  228.                                       && (p_data[i_size - 1] & DATA_INDICATOR))
  229.                                         ?  VLC_TRUE : VLC_FALSE;
  230.     Dump( VLC_FALSE, p_data, i_size );
  231.     if ( pi_size == NULL )
  232.         free( p_data );
  233.     else
  234.         *pi_size = i_size;
  235.     return VLC_SUCCESS;
  236. }
  237. /*
  238.  * Session layer
  239.  */
  240. #define ST_SESSION_NUMBER           0x90
  241. #define ST_OPEN_SESSION_REQUEST     0x91
  242. #define ST_OPEN_SESSION_RESPONSE    0x92
  243. #define ST_CREATE_SESSION           0x93
  244. #define ST_CREATE_SESSION_RESPONSE  0x94
  245. #define ST_CLOSE_SESSION_REQUEST    0x95
  246. #define ST_CLOSE_SESSION_RESPONSE   0x96
  247. #define SS_OK             0x00
  248. #define SS_NOT_ALLOCATED  0xF0
  249. #define RI_RESOURCE_MANAGER            0x00010041
  250. #define RI_APPLICATION_INFORMATION     0x00020041
  251. #define RI_CONDITIONAL_ACCESS_SUPPORT  0x00030041
  252. #define RI_HOST_CONTROL                0x00200041
  253. #define RI_DATE_TIME                   0x00240041
  254. #define RI_MMI                         0x00400041
  255. static int ResourceIdToInt( uint8_t *p_data )
  256. {
  257.     return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
  258.             | ((int)p_data[2] << 8) | p_data[3];
  259. }
  260. /*****************************************************************************
  261.  * SPDUSend
  262.  *****************************************************************************/
  263. static int SPDUSend( access_t * p_access, int i_session_id,
  264.                      uint8_t *p_data, int i_size )
  265. {
  266.     access_sys_t *p_sys = p_access->p_sys;
  267.     uint8_t *p_spdu = malloc( i_size + 4 );
  268.     uint8_t *p = p_spdu;
  269.     uint8_t i_tag;
  270.     uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
  271.     *p++ = ST_SESSION_NUMBER;
  272.     *p++ = 0x02;
  273.     *p++ = (i_session_id >> 8);
  274.     *p++ = i_session_id & 0xff;
  275.     memcpy( p, p_data, i_size );
  276.     i_size += 4;
  277.     p = p_spdu;
  278.     while ( i_size > 0 )
  279.     {
  280.         if ( i_size > MAX_TPDU_DATA )
  281.         {
  282.             if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
  283.                            MAX_TPDU_DATA ) != VLC_SUCCESS )
  284.             {
  285.                 msg_Err( p_access, "couldn't send TPDU on session %d",
  286.                          i_session_id );
  287.                 free( p_spdu );
  288.                 return VLC_EGENERIC;
  289.             }
  290.             p += MAX_TPDU_DATA;
  291.             i_size -= MAX_TPDU_DATA;
  292.         }
  293.         else
  294.         {
  295.             if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
  296.                     != VLC_SUCCESS )
  297.             {
  298.                 msg_Err( p_access, "couldn't send TPDU on session %d",
  299.                          i_session_id );
  300.                 free( p_spdu );
  301.                 return VLC_EGENERIC;
  302.             }
  303.             i_size = 0;
  304.         }
  305.         if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
  306.                || i_tag != T_SB )
  307.         {
  308.             msg_Err( p_access, "couldn't recv TPDU on session %d",
  309.                      i_session_id );
  310.             free( p_spdu );
  311.             return VLC_EGENERIC;
  312.         }
  313.     }
  314.     free( p_spdu );
  315.     return VLC_SUCCESS;
  316. }
  317. /*****************************************************************************
  318.  * SessionOpen
  319.  *****************************************************************************/
  320. static void SessionOpen( access_t * p_access, uint8_t i_slot,
  321.                          uint8_t *p_spdu, int i_size )
  322. {
  323.     access_sys_t *p_sys = p_access->p_sys;
  324.     int i_session_id;
  325.     int i_resource_id = ResourceIdToInt( &p_spdu[2] );
  326.     uint8_t p_response[16];
  327.     int i_status = SS_NOT_ALLOCATED;
  328.     uint8_t i_tag;
  329.     for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
  330.     {
  331.         if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
  332.             break;
  333.     }
  334.     if ( i_session_id == MAX_SESSIONS )
  335.     {
  336.         msg_Err( p_access, "too many sessions !" );
  337.         return;
  338.     }
  339.     p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
  340.     p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
  341.     p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
  342.     p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
  343.     if ( i_resource_id == RI_RESOURCE_MANAGER
  344.           || i_resource_id == RI_APPLICATION_INFORMATION
  345.           || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
  346.           || i_resource_id == RI_DATE_TIME
  347.           || i_resource_id == RI_MMI )
  348.     {
  349.         i_status = SS_OK;
  350.     }
  351.     p_response[0] = ST_OPEN_SESSION_RESPONSE;
  352.     p_response[1] = 0x7;
  353.     p_response[2] = i_status;
  354.     p_response[3] = p_spdu[2];
  355.     p_response[4] = p_spdu[3];
  356.     p_response[5] = p_spdu[4];
  357.     p_response[6] = p_spdu[5];
  358.     p_response[7] = i_session_id >> 8;
  359.     p_response[8] = i_session_id & 0xff;
  360.     if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) !=
  361.             VLC_SUCCESS )
  362.     {
  363.         msg_Err( p_access,
  364.                  "SessionOpen: couldn't send TPDU on slot %d", i_slot );
  365.         return;
  366.     }
  367.     if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
  368.     {
  369.         msg_Err( p_access,
  370.                  "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
  371.         return;
  372.     }
  373.     switch ( i_resource_id )
  374.     {
  375.     case RI_RESOURCE_MANAGER:
  376.         ResourceManagerOpen( p_access, i_session_id ); break; 
  377.     case RI_APPLICATION_INFORMATION:
  378.         ApplicationInformationOpen( p_access, i_session_id ); break; 
  379.     case RI_CONDITIONAL_ACCESS_SUPPORT:
  380.         ConditionalAccessOpen( p_access, i_session_id ); break; 
  381.     case RI_DATE_TIME:
  382.         DateTimeOpen( p_access, i_session_id ); break; 
  383.     case RI_MMI:
  384.         MMIOpen( p_access, i_session_id ); break; 
  385.     case RI_HOST_CONTROL:
  386.     default:
  387.         msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
  388.         p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
  389.     }
  390. }
  391. /*****************************************************************************
  392.  * SessionClose
  393.  *****************************************************************************/
  394. static void SessionClose( access_t * p_access, int i_session_id )
  395. {
  396.     access_sys_t *p_sys = p_access->p_sys;
  397.     uint8_t p_response[16];
  398.     uint8_t i_tag;
  399.     uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
  400.     if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
  401.         p_sys->p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
  402.     p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
  403.     p_response[0] = ST_CLOSE_SESSION_RESPONSE;
  404.     p_response[1] = 0x3;
  405.     p_response[2] = SS_OK;
  406.     p_response[3] = i_session_id >> 8;
  407.     p_response[4] = i_session_id & 0xff;
  408.     if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
  409.             VLC_SUCCESS )
  410.     {
  411.         msg_Err( p_access,
  412.                  "SessionOpen: couldn't send TPDU on slot %d", i_slot );
  413.         return;
  414.     }
  415.     if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
  416.     {
  417.         msg_Err( p_access,
  418.                  "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
  419.         return;
  420.     }
  421. }
  422. /*****************************************************************************
  423.  * SPDUHandle
  424.  *****************************************************************************/
  425. static void SPDUHandle( access_t * p_access, uint8_t i_slot,
  426.                         uint8_t *p_spdu, int i_size )
  427. {
  428.     access_sys_t *p_sys = p_access->p_sys;
  429.     int i_session_id;
  430.     switch ( p_spdu[0] )
  431.     {
  432.     case ST_SESSION_NUMBER:
  433.         if ( i_size <= 4 )
  434.             return;
  435.         i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
  436.         p_sys->p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
  437.                                                        p_spdu + 4, i_size - 4 );
  438.         break;
  439.     case ST_OPEN_SESSION_REQUEST:
  440.         if ( i_size != 6 || p_spdu[1] != 0x4 )
  441.             return;
  442.         SessionOpen( p_access, i_slot, p_spdu, i_size );
  443.         break;
  444.     case ST_CLOSE_SESSION_REQUEST:
  445.         i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
  446.         SessionClose( p_access, i_session_id );
  447.         break;
  448.     default:
  449.         break;
  450.     }
  451. }
  452. /*
  453.  * Application layer
  454.  */
  455. #define AOT_NONE                    0x000000
  456. #define AOT_PROFILE_ENQ             0x9F8010
  457. #define AOT_PROFILE                 0x9F8011
  458. #define AOT_PROFILE_CHANGE          0x9F8012
  459. #define AOT_APPLICATION_INFO_ENQ    0x9F8020
  460. #define AOT_APPLICATION_INFO        0x9F8021
  461. #define AOT_ENTER_MENU              0x9F8022
  462. #define AOT_CA_INFO_ENQ             0x9F8030
  463. #define AOT_CA_INFO                 0x9F8031
  464. #define AOT_CA_PMT                  0x9F8032
  465. #define AOT_CA_PMT_REPLY            0x9F8033
  466. #define AOT_TUNE                    0x9F8400
  467. #define AOT_REPLACE                 0x9F8401
  468. #define AOT_CLEAR_REPLACE           0x9F8402
  469. #define AOT_ASK_RELEASE             0x9F8403
  470. #define AOT_DATE_TIME_ENQ           0x9F8440
  471. #define AOT_DATE_TIME               0x9F8441
  472. #define AOT_CLOSE_MMI               0x9F8800
  473. #define AOT_DISPLAY_CONTROL         0x9F8801
  474. #define AOT_DISPLAY_REPLY           0x9F8802
  475. #define AOT_TEXT_LAST               0x9F8803
  476. #define AOT_TEXT_MORE               0x9F8804
  477. #define AOT_KEYPAD_CONTROL          0x9F8805
  478. #define AOT_KEYPRESS                0x9F8806
  479. #define AOT_ENQ                     0x9F8807
  480. #define AOT_ANSW                    0x9F8808
  481. #define AOT_MENU_LAST               0x9F8809
  482. #define AOT_MENU_MORE               0x9F880A
  483. #define AOT_MENU_ANSW               0x9F880B
  484. #define AOT_LIST_LAST               0x9F880C
  485. #define AOT_LIST_MORE               0x9F880D
  486. #define AOT_SUBTITLE_SEGMENT_LAST   0x9F880E
  487. #define AOT_SUBTITLE_SEGMENT_MORE   0x9F880F
  488. #define AOT_DISPLAY_MESSAGE         0x9F8810
  489. #define AOT_SCENE_END_MARK          0x9F8811
  490. #define AOT_SCENE_DONE              0x9F8812
  491. #define AOT_SCENE_CONTROL           0x9F8813
  492. #define AOT_SUBTITLE_DOWNLOAD_LAST  0x9F8814
  493. #define AOT_SUBTITLE_DOWNLOAD_MORE  0x9F8815
  494. #define AOT_FLUSH_DOWNLOAD          0x9F8816
  495. #define AOT_DOWNLOAD_REPLY          0x9F8817
  496. #define AOT_COMMS_CMD               0x9F8C00
  497. #define AOT_CONNECTION_DESCRIPTOR   0x9F8C01
  498. #define AOT_COMMS_REPLY             0x9F8C02
  499. #define AOT_COMMS_SEND_LAST         0x9F8C03
  500. #define AOT_COMMS_SEND_MORE         0x9F8C04
  501. #define AOT_COMMS_RCV_LAST          0x9F8C05
  502. #define AOT_COMMS_RCV_MORE          0x9F8C06
  503. /*****************************************************************************
  504.  * APDUGetTag
  505.  *****************************************************************************/
  506. static int APDUGetTag( const uint8_t *p_apdu, int i_size )
  507. {
  508.     if ( i_size >= 3 )
  509.     {
  510.         int i, t = 0;
  511.         for ( i = 0; i < 3; i++ )
  512.             t = (t << 8) | *p_apdu++;
  513.         return t;
  514.     }
  515.     return AOT_NONE;
  516. }
  517. /*****************************************************************************
  518.  * APDUGetLength
  519.  *****************************************************************************/
  520. static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
  521. {
  522.     return GetLength( &p_apdu[3], pi_size );
  523. }
  524. /*****************************************************************************
  525.  * APDUSend
  526.  *****************************************************************************/
  527. static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
  528.                      uint8_t *p_data, int i_size )
  529. {
  530.     uint8_t *p_apdu = malloc( i_size + 12 );
  531.     uint8_t *p = p_apdu;
  532.     int i_ret;
  533.     *p++ = (i_tag >> 16);
  534.     *p++ = (i_tag >> 8) & 0xff;
  535.     *p++ = i_tag & 0xff;
  536.     p = SetLength( p, i_size );
  537.     if ( i_size )
  538.         memcpy( p, p_data, i_size );
  539.     i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
  540.     free( p_apdu );
  541.     return i_ret;
  542. }
  543. /*****************************************************************************
  544.  * ResourceManagerHandle
  545.  *****************************************************************************/
  546. static void ResourceManagerHandle( access_t * p_access, int i_session_id,
  547.                                    uint8_t *p_apdu, int i_size )
  548. {
  549.     int i_tag = APDUGetTag( p_apdu, i_size );
  550.     switch ( i_tag )
  551.     {
  552.     case AOT_PROFILE_ENQ:
  553.     {
  554.         int resources[] = { htonl(RI_RESOURCE_MANAGER),
  555.                             htonl(RI_APPLICATION_INFORMATION),
  556.                             htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
  557.                             htonl(RI_DATE_TIME),
  558.                             htonl(RI_MMI)
  559.                           };
  560.         APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
  561.                   sizeof(resources) );
  562.         break;
  563.     }
  564.     case AOT_PROFILE:
  565.         APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
  566.         break;
  567.     default:
  568.         msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
  569.                  i_tag );
  570.     }
  571. }
  572. /*****************************************************************************
  573.  * ResourceManagerOpen
  574.  *****************************************************************************/
  575. static void ResourceManagerOpen( access_t * p_access, int i_session_id )
  576. {
  577.     access_sys_t *p_sys = p_access->p_sys;
  578.     msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
  579.     p_sys->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
  580.     APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
  581. }
  582. /*****************************************************************************
  583.  * ApplicationInformationHandle
  584.  *****************************************************************************/
  585. static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
  586.                                           uint8_t *p_apdu, int i_size )
  587. {
  588.     int i_tag = APDUGetTag( p_apdu, i_size );
  589.     switch ( i_tag )
  590.     {
  591.     case AOT_APPLICATION_INFO:
  592.     {
  593.         int i_type, i_manufacturer, i_code;
  594.         int l = 0;
  595.         uint8_t *d = APDUGetLength( p_apdu, &l );
  596.         if ( l < 4 ) break;
  597.         p_apdu[l + 3] = '';
  598.         i_type = *d++;
  599.         i_manufacturer = ((int)d[0] << 8) | d[1];
  600.         d += 2;
  601.         i_code = ((int)d[0] << 8) | d[1];
  602.         d += 2;
  603.         d = GetLength( d, &l );
  604.         d[l] = '';
  605.         msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
  606.                   d, i_type, i_manufacturer, i_code );
  607.         break;
  608.     }
  609.     default:
  610.         msg_Err( p_access,
  611.                  "unexpected tag in ApplicationInformationHandle (0x%x)",
  612.                  i_tag );
  613.     }
  614. }
  615. /*****************************************************************************
  616.  * ApplicationInformationOpen
  617.  *****************************************************************************/
  618. static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
  619. {
  620.     access_sys_t *p_sys = p_access->p_sys;
  621.     msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
  622.     p_sys->p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
  623.     APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
  624. }
  625. /*****************************************************************************
  626.  * ConditionalAccessHandle
  627.  *****************************************************************************/
  628. static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
  629.                                      uint8_t *p_apdu, int i_size )
  630. {
  631.     access_sys_t *p_sys = p_access->p_sys;
  632.     int i_tag = APDUGetTag( p_apdu, i_size );
  633.     switch ( i_tag )
  634.     {
  635.     case AOT_CA_INFO:
  636.     {
  637.         if ( p_sys->i_nb_capmts )
  638.         {
  639.             int i;
  640.             msg_Dbg( p_access, "sending CAPMT on session %d", i_session_id );
  641.             for ( i = 0; i < p_sys->i_nb_capmts; i++ )
  642.             {
  643.                 int i_size;
  644.                 uint8_t *p;
  645.                 p = GetLength( &p_sys->pp_capmts[i][3], &i_size );
  646.                 SPDUSend( p_access, i_session_id, p_sys->pp_capmts[i],
  647.                           i_size + (p - p_sys->pp_capmts[i]) );
  648.             }
  649.             p_sys->i_ca_timeout = 100000;
  650.         }
  651.         break;
  652.     }
  653.     default:
  654.         msg_Err( p_access,
  655.                  "unexpected tag in ConditionalAccessHandle (0x%x)",
  656.                  i_tag );
  657.     }
  658. }
  659. /*****************************************************************************
  660.  * ConditionalAccessOpen
  661.  *****************************************************************************/
  662. static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
  663. {
  664.     access_sys_t *p_sys = p_access->p_sys;
  665.     msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
  666.     p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
  667.     APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
  668. }
  669. typedef struct
  670. {
  671.     int i_interval;
  672.     mtime_t i_last;
  673. } date_time_t;
  674. /*****************************************************************************
  675.  * DateTimeSend
  676.  *****************************************************************************/
  677. static void DateTimeSend( access_t * p_access, int i_session_id )
  678. {
  679.     access_sys_t *p_sys = p_access->p_sys;
  680.     date_time_t *p_date =
  681.         (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
  682.     time_t t = time(NULL);
  683.     struct tm tm_gmt;
  684.     struct tm tm_loc;
  685.     if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
  686.     {
  687.         int Y = tm_gmt.tm_year;
  688.         int M = tm_gmt.tm_mon + 1;
  689.         int D = tm_gmt.tm_mday;
  690.         int L = (M == 1 || M == 2) ? 1 : 0;
  691.         int MJD = 14956 + D + (int)((Y - L) * 365.25)
  692.                     + (int)((M + 1 + L * 12) * 30.6001);
  693.         uint8_t p_response[7];
  694. #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
  695.         p_response[0] = htons(MJD) >> 8;
  696.         p_response[1] = htons(MJD) & 0xff;
  697.         p_response[2] = DEC2BCD(tm_gmt.tm_hour);
  698.         p_response[3] = DEC2BCD(tm_gmt.tm_min);
  699.         p_response[4] = DEC2BCD(tm_gmt.tm_sec);
  700.         p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
  701.         p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
  702.         APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
  703.         p_date->i_last = mdate();
  704.     }
  705. }
  706. /*****************************************************************************
  707.  * DateTimeHandle
  708.  *****************************************************************************/
  709. static void DateTimeHandle( access_t * p_access, int i_session_id,
  710.                             uint8_t *p_apdu, int i_size )
  711. {
  712.     access_sys_t *p_sys = p_access->p_sys;
  713.     date_time_t *p_date =
  714.         (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
  715.     int i_tag = APDUGetTag( p_apdu, i_size );
  716.     switch ( i_tag )
  717.     {
  718.     case AOT_DATE_TIME_ENQ:
  719.     {
  720.         int l;
  721.         const uint8_t *d = APDUGetLength( p_apdu, &l );
  722.         if ( l > 0 )
  723.         {
  724.             p_date->i_interval = *d;
  725.             msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
  726.                      p_date->i_interval );
  727.         }
  728.         else
  729.             p_date->i_interval = 0;
  730.         DateTimeSend( p_access, i_session_id );
  731.         break;
  732.     }
  733.     default:
  734.         msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
  735.     }
  736. }
  737. /*****************************************************************************
  738.  * DateTimeManage
  739.  *****************************************************************************/
  740. static void DateTimeManage( access_t * p_access, int i_session_id )
  741. {
  742.     access_sys_t *p_sys = p_access->p_sys;
  743.     date_time_t *p_date =
  744.         (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
  745.     if ( p_date->i_interval
  746.           && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
  747.     {
  748.         DateTimeSend( p_access, i_session_id );
  749.     }
  750. }
  751. /*****************************************************************************
  752.  * DateTimeOpen
  753.  *****************************************************************************/
  754. static void DateTimeOpen( access_t * p_access, int i_session_id )
  755. {
  756.     access_sys_t *p_sys = p_access->p_sys;
  757.     msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
  758.     p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
  759.     p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
  760.     p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t));
  761.     memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
  762.     DateTimeSend( p_access, i_session_id );
  763. }
  764. /*****************************************************************************
  765.  * MMIHandle
  766.  *****************************************************************************/
  767. static void MMIHandle( access_t * p_access, int i_session_id,
  768.                             uint8_t *p_apdu, int i_size )
  769. {
  770.     int i_tag = APDUGetTag( p_apdu, i_size );
  771.     switch ( i_tag )
  772.     {
  773.     default:
  774.         msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
  775.     }
  776. }
  777. /*****************************************************************************
  778.  * MMIOpen
  779.  *****************************************************************************/
  780. static void MMIOpen( access_t * p_access, int i_session_id )
  781. {
  782.     access_sys_t *p_sys = p_access->p_sys;
  783.     msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
  784.     p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
  785. }
  786. /*
  787.  * Hardware handling
  788.  */
  789. /*****************************************************************************
  790.  * InitSlot: Open the transport layer
  791.  *****************************************************************************/
  792. #define MAX_TC_RETRIES 20
  793. static int InitSlot( access_t * p_access, int i_slot )
  794. {
  795.     access_sys_t *p_sys = p_access->p_sys;
  796.     int i;
  797.     if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
  798.             != VLC_SUCCESS )
  799.     {
  800.         msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
  801.                  i_slot );
  802.         return VLC_EGENERIC;
  803.     }
  804.     /* This is out of the spec */
  805.     for ( i = 0; i < MAX_TC_RETRIES; i++ )
  806.     {
  807.         uint8_t i_tag;
  808.         if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
  809.               && i_tag == T_CTC_REPLY )
  810.         {
  811.             p_sys->pb_active_slot[i_slot] = VLC_TRUE;
  812.             break;
  813.         }
  814.         if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
  815.                 != VLC_SUCCESS )
  816.         {
  817.             msg_Err( p_access,
  818.                      "en50221_Init: couldn't send TPDU on slot %d",
  819.                      i_slot );
  820.             continue;
  821.         }
  822.     }
  823.     if ( p_sys->pb_active_slot[i_slot] )
  824.     {
  825.         p_sys->i_ca_timeout = 1000;
  826.         return VLC_SUCCESS;
  827.     }
  828.     return VLC_EGENERIC;
  829. }
  830. /*
  831.  * External entry points
  832.  */
  833. /*****************************************************************************
  834.  * en50221_Poll : Poll the CAM for TPDUs
  835.  *****************************************************************************/
  836. int E_(en50221_Poll)( access_t * p_access )
  837. {
  838.     access_sys_t *p_sys = p_access->p_sys;
  839.     int i_slot;
  840.     int i_session_id;
  841.     for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
  842.     {
  843.         uint8_t i_tag;
  844.         if ( !p_sys->pb_active_slot[i_slot] )
  845.         {
  846.             ca_slot_info_t sinfo;
  847.             sinfo.num = i_slot;
  848.             if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
  849.             {
  850.                 msg_Err( p_access, "en50221_Poll: couldn't get info on slot %d",
  851.                          i_slot );
  852.                 continue;
  853.             }
  854.             if ( sinfo.flags & CA_CI_MODULE_READY )
  855.             {
  856.                 msg_Dbg( p_access, "en50221_Poll: slot %d is active",
  857.                          i_slot );
  858.                 p_sys->pb_active_slot[i_slot] = VLC_TRUE;
  859.             }
  860.             else
  861.                 continue;
  862.             InitSlot( p_access, i_slot );
  863.         }
  864.         if ( !p_sys->pb_tc_has_data[i_slot] )
  865.         {
  866.             if ( TPDUSend( p_access, i_slot, T_DATA_LAST, NULL, 0 ) !=
  867.                     VLC_SUCCESS )
  868.             {
  869.                 msg_Err( p_access,
  870.                          "en50221_Poll: couldn't send TPDU on slot %d",
  871.                          i_slot );
  872.                 continue;
  873.             }
  874.             if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) !=
  875.                     VLC_SUCCESS )
  876.             {
  877.                 msg_Err( p_access,
  878.                          "en50221_Poll: couldn't recv TPDU on slot %d",
  879.                          i_slot );
  880.                 continue;
  881.             }
  882.         }
  883.         while ( p_sys->pb_tc_has_data[i_slot] )
  884.         {
  885.             uint8_t p_tpdu[MAX_TPDU_SIZE];
  886.             int i_size, i_session_size;
  887.             uint8_t *p_session;
  888.             if ( TPDUSend( p_access, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
  889.             {
  890.                 msg_Err( p_access,
  891.                          "en50221_Poll: couldn't send TPDU on slot %d",
  892.                          i_slot );
  893.                 continue;
  894.             }
  895.             if ( TPDURecv( p_access, i_slot, &i_tag, p_tpdu, &i_size ) !=
  896.                     VLC_SUCCESS )
  897.             {
  898.                 msg_Err( p_access,
  899.                          "en50221_Poll: couldn't recv TPDU on slot %d",
  900.                          i_slot );
  901.                 continue;
  902.             }
  903.             p_session = GetLength( &p_tpdu[3], &i_session_size );
  904.             if ( i_session_size <= 1 )
  905.                 continue;
  906.             p_session++;
  907.             i_session_size--;
  908.             if ( i_tag != T_DATA_LAST )
  909.             {
  910.                 msg_Err( p_access,
  911.                          "en50221_Poll: fragmented TPDU not supported" );
  912.                 break;
  913.             }
  914.             SPDUHandle( p_access, i_slot, p_session, i_session_size );
  915.         }
  916.     }
  917.     for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
  918.     {
  919.         if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
  920.               && p_sys->p_sessions[i_session_id - 1].pf_manage )
  921.         {
  922.             p_sys->p_sessions[i_session_id - 1].pf_manage( p_access,
  923.                                                            i_session_id );
  924.         }
  925.     }
  926.     return VLC_SUCCESS;
  927. }
  928. /*****************************************************************************
  929.  * en50221_SetCAPMT :
  930.  *****************************************************************************/
  931. int E_(en50221_SetCAPMT)( access_t * p_access, uint8_t **pp_capmts,
  932.                           int i_nb_capmts )
  933. {
  934.     access_sys_t *p_sys = p_access->p_sys;
  935.     int i_session_id;
  936.     for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
  937.     {
  938.         int i;
  939.         if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
  940.               != RI_CONDITIONAL_ACCESS_SUPPORT )
  941.             continue;
  942.         msg_Dbg( p_access, "sending CAPMT on session %d", i_session_id );
  943.         for ( i = 0; i < i_nb_capmts; i++ )
  944.         {
  945.             int i_size;
  946.             uint8_t *p;
  947.             p = GetLength( &pp_capmts[i][3], &i_size );
  948.             SPDUSend( p_access, i_session_id, pp_capmts[i],
  949.                       i_size + (p - pp_capmts[i]) );
  950.         }
  951.         p_sys->i_ca_timeout = 100000;
  952.     }
  953.     if ( p_sys->i_nb_capmts )
  954.     {
  955.         int i;
  956.         for ( i = 0; i < p_sys->i_nb_capmts; i++ )
  957.         {
  958.             free( p_sys->pp_capmts[i] );
  959.         }
  960.         free( p_sys->pp_capmts );
  961.     }
  962.     p_sys->pp_capmts = pp_capmts;
  963.     p_sys->i_nb_capmts = i_nb_capmts;
  964.     return VLC_SUCCESS;
  965. }
  966. /*****************************************************************************
  967.  * en50221_End :
  968.  *****************************************************************************/
  969. void E_(en50221_End)( access_t * p_access )
  970. {
  971.     access_sys_t *p_sys = p_access->p_sys;
  972.     if ( p_sys->i_nb_capmts )
  973.     {
  974.         int i;
  975.         for ( i = 0; i < p_sys->i_nb_capmts; i++ )
  976.         {
  977.             free( p_sys->pp_capmts[i] );
  978.         }
  979.         free( p_sys->pp_capmts );
  980.     }
  981.     /* TODO */
  982. }