Support

Plugin API

The plugin API provides a way to interface smart cards into ExPVR. Plugins are placed in the plugins directory located in the programs installation location (usually C:\Program Files\ph-mb\ExPVR). For the plugins to work 'ffdecsa_32_int.dll' must be placed in the 'CSA Engines' directory, it is quite easy to find this using google.

Full documentation of the API is available at http://www.ph-mb.com/downloads/ExPVR/ExPVR_Plugin_API.zip.

Each plugin must reference 'pHM.DVBLib.SmartCard.Interfaces.dll'. This contains the interfaces that your class must implement and some helper classes. You also will need to reference 'pHMb.XTV' and 'pHM.DVBLib.Common.DataType', these contain some data types which are required.

The first interface you need to implement is 'ISmartCard', an example is below:

    using pHM.DVBLib.SmartCard.Interfaces;
    public class MySmartCard : ISmartCard
    {
        #region ISmartCard Members
        public string CardName
        {
            get
            {
                return "My Smart Card"; 
            }
        }

        public string CardVersion
        {
            get 
            {
                return "2";
            }
        }

        public ISmartCardConnection GetNewCardConnection()
        {
            return new SmartCardConnection();
        }
        #endregion
    }

This class identifies your plugin to the application. CardName should be descriptive name of the card type your plugin is for. Version should be the version of the card type. You can name the class what you want as long as it implements ISmartCard.

The second interface is 'ISmartCardConnection':

    using pHM.DVBLib.SmartCard.Interfaces;
    using pHM.DVBLib.Common.DataTypes;
    using pHMb.XTV;
    public class SmartCardConnection : ISmartCardConnection
    {
        #region Private Variables
        private ISO_7816Connection _cardConnection;
        private CommandSet _commandSet;

        #endregion

        #region ISmartCardConnection Members
        #region ISmartCardConnection Public Properties
        /// <summary>
        /// Gets the underlying ISO_7816Connection
        /// </summary>
        public ISO_7816Connection CardConnection
        {
            get
            {
                return _cardConnection;
            }
        }

        /// <summary>
        /// Return true if the card is ready to decrypt code words
        /// </summary>
        public bool isInitialized 
        {
            get
            {
                return _commandSet.isInitialized;
            }
        }

        /// <summary>
        /// The answer to reset of the viewing card
        /// </summary>
        public byte[] HistoricalBytes
        {
            get 
            {
                return _cardConnection.HistoricalBytes;
            }
        }

        /// <summary>
        /// The card ID / viewing card number
        /// </summary>
        public string CardID
        {
            get 
            { 
                return _cardID; 
            }
        }

        /// <summary>
        /// The box id stored in the card
        /// </summary>
        public string BoxID
        {
            get 
            {
                return _boxID;
            }
        }

        /// <summary>
        /// The cards caid
        /// </summary>
        public uint CaId 
        { 
            get
            {
                return _caId;
            }
        }
        #endregion

        #region ISmartCardConnection Events
        /// <summary>
        /// Event thrown when data is received from the card
        /// These are mainly used for debugging but should be used, they may have more
        /// uses in future
        /// This example does not throw them for simplicity
        /// </summary>
        public event EventHandler<Interfaces.DataTransferedEventArgs> DataReceived;

        /// <summary>
        /// Event thrown when data is sent to the card
        /// </summary>
        public event EventHandler<Interfaces.DataTransferedEventArgs> DataSent;
        #endregion

        #region ISmartCardConnection Methods
        /// <summary>
        /// Connect to the card
        /// </summary>
        /// <param name="cardConnection">An open ISO_7816Connection</param>
        /// <example>
        /// <code>
        /// Connect(new ISO_7186Connection("COM1"));
        /// </code>
        /// </example>
        public void Connect(ISO_7816Connection cardConnection)
        {
            _cardConnection = cardConnection;
        }

        /// <summary>
        /// Disconnect the card connection
        /// </summary>
        public void Disconnect()
        {
            _cardConnection.Disconnect();
        }

        /// <summary>
        /// Initialize the card to a state where ECMs can be decrypted
        /// </summary>
        public void InitializeCard()
        {
            // Do any card setup here
            // This will be run before recording decryption starts
            
            // Example:
            T0Command myCommand = new T0Command()
            {
                CommandClass = 0x01,
                Instruction = 0x02,
                P1 = 0x03,
                P2 = 0x04,
                P3 = 0x05
            };
            _cardConnection.SendT0Command(ref myCommand);
			
            // Status bytes 1 and 2 are saved in myCommand.SW1 and myCommand.SW2
            if (myCommand.SW1 != 90)
                throw new SmartCardStatusException("Card returned 'not ok'.", status);
           
            // Any returned data is saved in myCommand.Data;
        }

        /// <summary>
        /// Decrypts an ECM of a given type
        /// </summary>
        /// <param name="ecm">Byte array containing an ECM packet</param>
        /// <param name="ecmType">The type of ECM (as described by ECMTypes)</param>
        /// <returns>A code word object</returns>
        /// <exception cref="SmartCardStatusException">Occurs when the card returns 
        /// an unexpected status</exception>
        public CodeWordData DecryptEcm(byte[] ecm, Interfaces.EcmTypes ecmType)
        {
            // This gets called whenever a code word is needed
            // ecm can be one of three types, in ExPVR it is only ever EcmTypes.TECM
            //
            // This is a special 'transformed' ECM which gets 
            // generated by the card while the program is being
            // recorded. 
            // It's like this: ECM in transport stream -> Smart Card -> TECM saved 
            // on hard drive
            //
            // EcmTypes.CardEcm is an ECM without its header
            // EcmTypes.TransportStreamEcm is the full ECM from the transport stream
        	
            byte[] codeWordPacket = new byte[0];
            CodeWordData<DateTime> codeWordData = new CodeWordData<DateTime>();
            NdsEcmData ecmData;

            switch (ecmType)
            {
                case Interfaces.EcmTypes.CardEcm:
                    // Not relevent
                    break;
                    
                case Interfaces.EcmTypes.TransportStreamEcm:
                    // Not relevent
                    break;
                    
                case Interfaces.EcmTypes.TECM:
                    ecmData = XtvMetaData.ParseTransformedEcm(ecm);
                    myEcmSendFunction(ecmData.CardEcm);
                    codeWordData.CodeWord = myCodeWordRequestFunction();

                    codeWordData.isEven = ecmData.isEven;
                    codeWordData.CodeWordID = ecmData.Timestamp;
                    break;
            }

            return codeWordData; 
        }
        
        private void myEcmSendFunction(byte[] ecm)
        {
            // Send ecm to card, Example (note these are not real commands)
            T0Command myCommand = new T0Command()
            {
                CommandClass = 0x01,
                Instruction = 0x02,
                P1 = 0x03,
                P2 = 0x04,
                P3 = (byte)(ecm.Length)
            };
            // Set data payload
            command.Data = ecm;
            _cardConnection.SendT0Command(ref myCommand);
			
            // Status bytes 1 and 2 are saved in myCommand.SW1 and myCommand.SW2
            if (myCommand.SW1 != 90)
                throw new SmartCardStatusException("Card returned 'not ok'.", status);
        }
        
        private byte[] myCodeWordRequestFunction()
        {
            // Request code word from card, Example (note these are not real comands)  
            T0Command command = new T0Command()
            {
                CommandClass = 0x01,
                Instruction = 0x03,
                P1 = 0x04,
                P2 = 0x05,
                P3 = 0x10
            };
            _cardConnection.SendT0Command(ref myCommand);
           
            // Any returned data is saved in myCommand.Data;
            byte[] unProcessedCodeWord = myCommand.Data;
            
            // Process the returned data into the actual 8-byte code word here
            byte[] processedCodeWord = DoSomthing(unProcessedCodeWord);
           
            return processedCodeWord;
        }
        #endregion
        #endregion
    }

This contains the actual card command set. ISO_7816Connection is a class to talk to ISO-7816 cards (most smart cards) inserted into a phoenix interface. This class is quite simple to use, the example above demonstrates all of its functionality. After setting up the connection, commands can be sent to the card by passing a T0Command object to ISO_7816Connection.SendT0Command. This function then either sends the data contained in T0Command.Data, or inserts returned data into the same variable depending on the command type.

Note that only one plugin should be installed at one time, at the moment there is no way of selecting which plugin to use, ExPVR will just use the first one it finds.