en50221.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:75k
源码类别:

midi

开发平台:

Unix_Linux

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