Index: TVLibrary/TVLibrary/Implementations/DVB/ConditionalAccess/ConditionalAccess.cs
===================================================================
--- TVLibrary/TVLibrary/Implementations/DVB/ConditionalAccess/ConditionalAccess.cs	(revision 26036)
+++ TVLibrary/TVLibrary/Implementations/DVB/ConditionalAccess/ConditionalAccess.cs	(working copy)
@@ -61,6 +61,7 @@
     private readonly ConexantBDA _conexant;
     private readonly GenPixBDA _genpix;
     private readonly TeVii _TeVii;
+    private readonly DigitalDevices _DigitalDevices;
     
     private readonly IHardwareProvider _HWProvider;
 
@@ -233,6 +234,19 @@
           }
           Release.DisposeToNull(ref _TeVii);
 
+          // DigitalDevices support
+          _DigitalDevices = new DigitalDevices(tunerFilter);
+          if (_DigitalDevices.IsGenericBDAS)
+          {
+            _genericbdas = _DigitalDevices;
+            if (_DigitalDevices.IsSupported)
+            {
+              _ciMenu = _DigitalDevices;
+            }
+            return; // detected
+          }        
+          Release.DisposeToNull(ref _DigitalDevices);
+
           Log.Log.WriteFile("Check for Conexant based card");
           _conexant = new ConexantBDA(tunerFilter);
           if (_conexant.IsConexant)
@@ -651,6 +665,10 @@
           byte[] caPmt = info.caPMT.CaPmtStruct(out caPmtLen);
           return _knc.SendPMT(caPmt, caPmtLen);
         }
+        if (_DigitalDevices != null && _DigitalDevices.IsSupported == true)
+        {
+          return _DigitalDevices.SendServiceIdToCam(channel.ServiceId);
+        }
         if (_digitalEveryWhere != null)
         {
           return _digitalEveryWhere.SendPMTToFireDTV(_mapSubChannels);

Property changes on: TVLibrary\TVLibrary\Implementations\DVB\ConditionalAccess\DigitalDevices
___________________________________________________________________
Added: bugtraq:url
   + http://mantis.team-mediaportal.com/view.php?id=%BUGID%
Added: bugtraq:logregex
   + ([Ii]ssues?|[Mm]antis|[Ff]ix(ed)?(\sfor)?)[\s]?:?(\s*(,|and)?[#|\s]\d+)+|^(\d+):
([1-9]\d+)

Index: TVLibrary/TVLibrary/Implementations/DVB/ConditionalAccess/DigitalDevices/DigitalDevices.cs
===================================================================
--- TVLibrary/TVLibrary/Implementations/DVB/ConditionalAccess/DigitalDevices/DigitalDevices.cs	(revision 0)
+++ TVLibrary/TVLibrary/Implementations/DVB/ConditionalAccess/DigitalDevices/DigitalDevices.cs	(revision 0)
@@ -0,0 +1,671 @@
+#region Copyright (C) 2005-2010 Team MediaPortal
+
+// Copyright (C) 2005-2010 Team MediaPortal
+// http://www.team-mediaportal.com
+// 
+// MediaPortal is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+// 
+// MediaPortal is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with MediaPortal. If not, see <http://www.gnu.org/licenses/>.
+
+#endregion
+
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using DirectShowLib;
+using DirectShowLib.BDA;
+using TvLibrary.Channels;
+using TvLibrary.Interfaces;
+using System.Security;
+using System.Text;
+using System.Threading;
+using System.Collections.Generic;
+using TvLibrary.Hardware;
+
+namespace TvLibrary.Implementations.DVB
+{
+  /// <summary>
+  /// DigitalDevices CI control class
+  /// It derives from GenericBDAS for DiSEqC support.
+  /// </summary>
+  public partial class DigitalDevices : GenericBDAS, ICiMenuActions 
+  {
+    #region constants
+
+    public static List<String> CIFilterNames = new List<String>() { "Digital Devices Common Interface", "Mystique SaTiX-S2 Dual Common Interface" };
+    public static List<String> VendorNames = new List<String>() { "Digital Devices", "Mystique SaTiX-S2 Dual" };
+
+    [ComImport, SuppressUnmanagedCodeSecurity,
+     Guid("28F54685-06FD-11D2-B27A-00A0C9223196"),
+     InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+    public interface IKsControl
+    {
+      [PreserveSig]
+      int KsProperty(
+        [In]       ref KSMETHOD Property,
+        [In]       Int32 PropertyLength,
+        [In, Out]  IntPtr PropertyData,
+        [In]       Int32 DataLength,
+        [In, Out]  ref Int32 BytesReturned
+      );
+
+      [PreserveSig]
+      int KsMethod(
+          [In]       ref KSMETHOD Method,
+          [In]       Int32 MethodLength,
+          [In, Out]  IntPtr MethodData,
+          [In]       Int32 DataLength,
+          [In, Out]  ref Int32 BytesReturned
+      );
+
+      [PreserveSig]
+      int KsEvent(
+        [In, Optional]  ref KSMETHOD Event,
+        [In]            Int32 EventLength,
+        [In, Out]       IntPtr EventData,
+        [In]            Int32 DataLength,
+        [In, Out]       ref Int32 BytesReturned
+      );
+    }
+
+    public struct KSMETHOD
+    {
+      public Guid Set;
+      public Int32 Id;
+      public Int32 Flags;
+      public KSMETHOD(Guid set, Int32 id, Int32 flags)
+      {
+        Set = set;
+        Id = (Int32)id;
+        Flags = (Int32)flags;
+      }
+    }
+
+    const Int32 KSMETHOD_TYPE_SEND = 1;
+    const Int32 KSPROPERTY_TYPE_GET = 1;
+    const Int32 KSPROPERTY_TYPE_SET = 2;
+
+    Guid KSPROPERTYSET_DD_COMMON_INTERFACE = new Guid("0aa8a501-a240-11de-b130-000000004d56");
+    Guid KSMETHODSET_DD_CAM_CONTROL = new Guid("0aa8a511-a240-11de-b130-000000004d56");
+
+    enum KSPROPERTY_DD_COMMON_INTERFACE
+    {
+      KSPROPERTY_DD_DECRYPT_PROGRAM = 0,
+      KSPROPERTY_DD_CAM_MENU_TITLE = 1,
+    }
+
+    enum KSMETHOD_DD_CAM_CONTROL
+    {
+      KSMETHOD_DD_CAM_RESET,
+      KSMETHOD_DD_CAM_ENTER_MENU,
+      KSMETHOD_DD_CAM_CLOSE_MENU,
+      KSMETHOD_DD_CAM_GET_MENU,
+      KSMETHOD_DD_CAM_MENU_REPLY,
+      KSMETHOD_DD_CAM_ANSWER,
+    }
+
+    struct DD_CAM_MENU_TITLE
+    {
+      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
+      String MenuTitle;
+    }
+
+    struct DD_CAM_MENU_REPLY
+    {
+      public Int32 Id;
+      public Int32 Choice;
+    }
+
+    struct DD_CAM_TEXT_DATA
+    {
+      public Int32 Id;
+      public Int32 Length;
+      [MarshalAs(UnmanagedType.LPStr)]
+      public String Data;
+    }
+
+    struct DD_CAM_MENU_DATA
+    {
+      public Int32 Id;
+      public Int32 Type;
+      public Int32 NumChoices;
+      public Int32 Length;
+      public String Title;
+      public String SubTitle;
+      public String BottomText;
+      public List<String> Choices;
+    }
+
+    enum E_STATE { IDLE, WAIT_FOR_OPEN, WAIT_FOR_MENU, MENU_OPEN, LIST_OPEN, ENQ_OPEN, CAM_ERROR = 99 };
+
+    #endregion
+
+    #region variables
+
+    private readonly bool _isDigitalDevices;
+    private readonly IKsPropertySet _propertySet;
+    // CI menu related handlers
+    private bool StopThread;
+    private ICiMenuCallbacks ciMenuCallbacks;
+    private Thread CiMenuThread;
+    private IBaseFilter CiFilter;
+    private Int32 _MenuId;
+
+    #endregion
+
+    #region Constructor
+
+    /// <summary>
+    /// Initializes a new instance of the <see cref="DigitalDevices"/> class.
+    /// </summary>
+    /// <param name="tunerFilter">The tuner filter.</param>
+    public DigitalDevices(IBaseFilter tunerFilter)
+      : base(tunerFilter)
+    {
+      _CardName = String.Empty;
+
+      FilterInfo fInfo;
+      tunerFilter.QueryFilterInfo(out fInfo);
+
+      // check for all vendors names to support this hardware
+      foreach (String vendor in VendorNames)
+      {
+        if (fInfo.achName.StartsWith(vendor))
+        {
+          _CardName = vendor;
+          break;
+        }
+      }
+      // nothing found?
+      if (_CardName == String.Empty)
+        return;
+
+      try
+      {
+        foreach (String CIFilterName in CIFilterNames)
+        {
+          if (fInfo.pGraph.FindFilterByName(CIFilterName, out CiFilter) == 0)
+          {
+            _isDigitalDevices = true;
+            Log.Log.Debug(FormatMessage(" Common Interface found!"));
+            break; // found
+          }
+          else
+          {
+            Log.Log.Info(FormatMessage(" Common Interface NOT found!"));
+          }
+        }
+      }
+      catch { }
+    }
+
+    #endregion
+
+    #region IHardwareProvider members
+
+    /// <summary>
+    /// Gets a value indicating whether this instance supports CAM.
+    /// </summary>
+    /// <value>
+    /// 	<c>true</c> if this instance supports CAM; otherwise, <c>false</c>.
+    /// </value>
+    public bool IsSupported
+    {
+      get { return _isDigitalDevices; }
+    }
+
+    #endregion
+
+    #region ICiMenuActions Member
+
+    /// <summary>
+    /// Sends the ServiceID to CAM to start decryption.
+    /// </summary>
+    /// <param name="serviceId">Service ID</param>
+    /// <returns>true if successful</returns>
+    public bool SendServiceIdToCam(Int32 serviceId)
+    {
+      IKsControl pControl = CiFilter as IKsControl;
+      if (pControl == null) return false;
+
+      KSMETHOD KsProperty = new KSMETHOD(KSPROPERTYSET_DD_COMMON_INTERFACE,
+                               (Int32)KSPROPERTY_DD_COMMON_INTERFACE.KSPROPERTY_DD_DECRYPT_PROGRAM,
+                               KSPROPERTY_TYPE_SET);
+      Int32 paramSize = Marshal.SizeOf(sizeof(Int32));
+      Int32 dwReturned = 0;
+      // Initialize unmanged memory to hold the struct.
+      IntPtr pSid = Marshal.AllocHGlobal(paramSize);
+      Marshal.WriteInt32(pSid, serviceId);
+      try
+      {
+        Int32 hr = pControl.KsProperty(ref KsProperty, Marshal.SizeOf(KsProperty),
+                                    pSid, paramSize,
+                                    ref dwReturned);
+        Log.Log.Debug(FormatMessage(String.Format("--> Setting service id {0} for decrypting returned {1}", serviceId, hr)));
+        return (hr == 0);
+      }
+      finally
+      {
+        // Free the unmanaged memory.
+        Marshal.FreeHGlobal(pSid);
+      }
+    }
+
+    /// <summary>
+    /// Sets the callback handler
+    /// </summary>
+    /// <param name="ciMenuHandler"></param>
+    /// <returns></returns>
+    public bool SetCiMenuHandler(ICiMenuCallbacks ciMenuHandler)
+    {
+      if (ciMenuHandler != null)
+      {
+        ciMenuCallbacks = ciMenuHandler;
+        StartCiHandlerThread();
+        return true;
+      }
+      return false;
+    }
+
+    /// <summary>
+    /// Enters the CI menu
+    /// </summary>
+    /// <returns></returns>
+    public bool EnterCIMenu()
+    {
+      IKsControl pControl = CiFilter as IKsControl;
+      if (pControl == null) return false;
+
+      KSMETHOD KsMethod = new KSMETHOD(KSMETHODSET_DD_CAM_CONTROL,
+                                 (Int32)KSMETHOD_DD_CAM_CONTROL.KSMETHOD_DD_CAM_ENTER_MENU,
+                                 KSMETHOD_TYPE_SEND);
+
+      Int32 dwReturned = 0;
+      Int32 hr = pControl.KsMethod(ref KsMethod, Marshal.SizeOf(KsMethod),
+                                  IntPtr.Zero, 0,
+                                  ref dwReturned
+                              );
+      if (hr == 0)
+      {
+        _MenuId = 0; // reset
+      }
+      return hr == 0;
+    }
+
+    /// <summary>
+    /// Resets the CAM.
+    /// </summary>
+    /// <returns></returns>
+    public bool ResetCAM()
+    {
+      IKsControl pControl = CiFilter as IKsControl;
+      if (pControl == null) return false;
+
+      KSMETHOD KsMethod = new KSMETHOD(KSMETHODSET_DD_CAM_CONTROL,
+                                 (Int32)KSMETHOD_DD_CAM_CONTROL.KSMETHOD_DD_CAM_RESET,
+                                 KSMETHOD_TYPE_SEND);
+
+      Int32 dwReturned = 0;
+      Int32 hr = pControl.KsMethod(ref KsMethod, Marshal.SizeOf(KsMethod),
+                                  IntPtr.Zero, 0,
+                                  ref dwReturned
+                              );
+      return hr == 0;
+    }
+
+    /// <summary>
+    /// Closes the CI menu
+    /// </summary>
+    /// <returns></returns>
+    public bool CloseCIMenu()
+    {
+      IKsControl pControl = CiFilter as IKsControl;
+      if (pControl == null) return false;
+
+      KSMETHOD KsMethod = new KSMETHOD(KSMETHODSET_DD_CAM_CONTROL,
+                                 (Int32)KSMETHOD_DD_CAM_CONTROL.KSMETHOD_DD_CAM_CLOSE_MENU,
+                                 KSMETHOD_TYPE_SEND);
+
+      Int32 dwReturned = 0;
+      Int32 hr = pControl.KsMethod(ref KsMethod, Marshal.SizeOf(KsMethod),
+                                  IntPtr.Zero, 0,
+                                  ref dwReturned
+                              );
+      return hr == 0;
+    }
+
+    /// <summary>
+    /// Selects a CI menu entry
+    /// </summary>
+    /// <param name="choice">choice</param>
+    /// <returns></returns>
+    public bool SelectMenu(byte choice)
+    {
+      IKsControl pControl = CiFilter as IKsControl;
+      if (pControl == null) return false;
+
+      DD_CAM_MENU_REPLY Reply;
+
+      Reply.Id = _MenuId;
+      Reply.Choice = choice;
+
+      // Initialize unmanged memory to hold the struct.
+      IntPtr pReply = Marshal.AllocHGlobal(Marshal.SizeOf(Reply));
+      try
+      {
+        // Copy the struct to unmanaged memory.
+        Marshal.StructureToPtr(Reply, pReply, false);
+
+        KSMETHOD KsMethod = new KSMETHOD(KSMETHODSET_DD_CAM_CONTROL,
+                                 (Int32)KSMETHOD_DD_CAM_CONTROL.KSMETHOD_DD_CAM_MENU_REPLY,
+                                 KSMETHOD_TYPE_SEND);
+        Int32 dwReturned = 0;
+        Int32 hr = pControl.KsMethod(ref KsMethod, Marshal.SizeOf(KsMethod),
+                                    pReply, Marshal.SizeOf(Reply),
+                                    ref dwReturned
+                                );
+        return hr == 0;
+      }
+      finally
+      {
+        // Free the unmanaged memory.
+        Marshal.FreeHGlobal(pReply);
+      }
+    }
+
+    /// <summary>
+    /// Sends an answer after CI request
+    /// </summary>
+    /// <param name="Cancel">true to cancel</param>
+    /// <param name="Answer">Answer string</param>
+    /// <returns></returns>
+    public bool SendMenuAnswer(bool Cancel, string Answer)
+    {
+      IKsControl pControl = CiFilter as IKsControl;
+      if (pControl == null) return false;
+
+      // Initialize unmanged memory to hold the struct.
+      Int32 answerLength = (String.IsNullOrEmpty(Answer) ? 0 : Answer.Length);
+      Int32 bufferSize = 8 + answerLength +1;
+
+      if (bufferSize < 12) bufferSize = 12;
+
+      IntPtr pReply = Marshal.AllocHGlobal(bufferSize);
+      try
+      {
+        // Copy the struct to unmanaged memory.
+        Marshal.WriteInt32(pReply, 0, _MenuId);
+        if (answerLength > 0)
+        {
+          Marshal.WriteInt32(pReply, 4, (int)Answer.Length);
+          for (int i = 0; i < Answer.Length; i += 1)
+          {
+            Marshal.WriteByte(pReply, 8 + i, (byte)Answer[i]);
+          }
+          Marshal.WriteByte(pReply, 8 + Answer.Length, 0);
+        }
+        else
+        {
+          Marshal.WriteInt32(pReply, 4, 0);
+        }
+
+        //DVB_MMI.DumpBinary(pReply, 0, bufferSize);
+
+        KSMETHOD KsMethod = new KSMETHOD(KSMETHODSET_DD_CAM_CONTROL,
+                                 (Int32)KSMETHOD_DD_CAM_CONTROL.KSMETHOD_DD_CAM_ANSWER,
+                                 KSMETHOD_TYPE_SEND);
+        Int32 dwReturned = 0;
+        Int32 hr = pControl.KsMethod(ref KsMethod, Marshal.SizeOf(KsMethod),
+                                    pReply, bufferSize,
+                                    ref dwReturned
+                                );
+        return hr == 0;
+      }
+      finally
+      {
+        // Free the unmanaged memory.
+        Marshal.FreeHGlobal(pReply);
+      }
+    }
+
+    #endregion
+
+    #region MMI reading methods
+
+    bool GetMenuTitle(out DD_CAM_MENU_TITLE MenuTitle)
+    {
+      MenuTitle = new DD_CAM_MENU_TITLE();
+
+      IKsControl pControl = CiFilter as IKsControl;
+      if (pControl == null) return false;
+
+      KSMETHOD KsProperty = new KSMETHOD(KSPROPERTYSET_DD_COMMON_INTERFACE,
+                               (Int32)KSPROPERTY_DD_COMMON_INTERFACE.KSPROPERTY_DD_CAM_MENU_TITLE,
+                               KSPROPERTY_TYPE_GET);
+      Int32 ulMenuSize = Marshal.SizeOf(MenuTitle);
+      Int32 dwReturned = 0;
+      // Initialize unmanged memory to hold the struct.
+      IntPtr pTitle = Marshal.AllocHGlobal(Marshal.SizeOf(MenuTitle));
+      try
+      {
+        Int32 hr = pControl.KsProperty(ref KsProperty, Marshal.SizeOf(KsProperty),
+                                    pTitle, ulMenuSize,
+                                    ref dwReturned
+                                );
+
+        if (hr != 0 || dwReturned != ulMenuSize)
+          return false;
+
+        MenuTitle = (DD_CAM_MENU_TITLE)Marshal.PtrToStructure(pTitle, typeof(DD_CAM_MENU_TITLE));
+        return true;
+      }
+      finally
+      {
+        // Free the unmanaged memory.
+        Marshal.FreeHGlobal(pTitle);
+      }
+    }
+
+    bool GetCamMenuText(out DD_CAM_MENU_DATA CiMenu)
+    {
+      CiMenu = new DD_CAM_MENU_DATA();
+      IKsControl pControl = CiFilter as IKsControl;
+      if (pControl == null) return false;
+
+      // Initialize unmanged memory to hold the struct.
+      Int32 ulMenuSize = 2048;
+      IntPtr pCiMenu = Marshal.AllocHGlobal(ulMenuSize);
+      try
+      {
+        KSMETHOD KsMethod = new KSMETHOD(KSMETHODSET_DD_CAM_CONTROL,
+                                 (Int32)KSMETHOD_DD_CAM_CONTROL.KSMETHOD_DD_CAM_GET_MENU,
+                                 KSMETHOD_TYPE_SEND);
+        Int32 dwReturned = 0;
+        Int32 hr = pControl.KsMethod(ref KsMethod, Marshal.SizeOf(KsMethod),
+                                    pCiMenu, ulMenuSize,
+                                    ref dwReturned
+                                );
+        if (hr == 0)
+        {
+          Int32 offs = 0;
+          CiMenu.Id = Marshal.ReadInt32(pCiMenu, offs); offs += 4;
+          CiMenu.Type = Marshal.ReadInt32(pCiMenu, offs); offs += 4;
+          CiMenu.NumChoices = Marshal.ReadInt32(pCiMenu, offs); offs += 4;
+          CiMenu.Length = Marshal.ReadInt32(pCiMenu, offs); offs += 4;
+
+          // check if we already received this menu, then ignore it.
+          // otherwise we would do an endless processing.
+          if (CiMenu.Id == _MenuId)
+            return false;
+
+          //DVB_MMI.DumpBinary(pCiMenu, 0, dwReturned);
+
+          CiMenu.Choices = new List<String>();
+          for (int i = 0; i < CiMenu.NumChoices + 3; i++)
+          {
+            IntPtr newPtr = new IntPtr(pCiMenu.ToInt32() + offs);
+            String choice = Marshal.PtrToStringAnsi(newPtr);
+            switch (i)
+            {
+              case 0:
+                CiMenu.Title = choice;
+                break;
+              case 1:
+                CiMenu.SubTitle = choice;
+                break;
+              case 2:
+                CiMenu.BottomText = choice;
+                break;
+              default:
+                CiMenu.Choices.Add(choice);
+                break;
+            }
+            offs += choice.Length + 1;
+          }
+          // remember current received menu id
+          _MenuId = CiMenu.Id;
+        }
+        //else
+        //  Marshal.ThrowExceptionForHR(hr);
+
+        return hr == 0;
+      }
+      finally
+      {
+        // Free the unmanaged memory.
+        Marshal.FreeHGlobal(pCiMenu);
+      }
+    }
+
+    /// <summary>
+    /// Process the MMI and do callbacks.
+    /// </summary>
+    /// <param name="CiMenu"></param>
+    /// <returns></returns>
+    bool ProcessCamMenu(DD_CAM_MENU_DATA CiMenu)
+    {
+      Log.Log.Debug("Menu received (ID {0} Type {1} Choices {2})", CiMenu.Id, CiMenu.Type, CiMenu.NumChoices);
+      //Log.Log.Debug(" Menu Id      = {0}", CiMenu.Id);
+      //Log.Log.Debug(" Menu Type    = {0}", CiMenu.Type);
+      //Log.Log.Debug(" Menu Choices = {0}", CiMenu.NumChoices);
+      //Log.Log.Debug(" Menu Length  = {0}", CiMenu.Length);
+
+      if (ciMenuCallbacks == null)
+        return false;
+
+      switch (CiMenu.Type)
+      {
+        case 1:
+        case 2:
+          ciMenuCallbacks.OnCiMenu(CiMenu.Title, CiMenu.SubTitle, CiMenu.BottomText, CiMenu.NumChoices);
+          int n = 0;
+          foreach (String choice in CiMenu.Choices)
+          {
+            ciMenuCallbacks.OnCiMenuChoice(n++, choice);
+          }
+          break;
+        case 3:
+        case 4:
+          ciMenuCallbacks.OnCiRequest(false, (uint)CiMenu.NumChoices, CiMenu.Title);
+          break;
+        default:
+          Log.Log.Debug("Unknown MMI Type {0}", CiMenu.Type);
+          break;
+      }
+      return true;
+    }
+
+    #endregion
+
+    #region CiMenuHandlerThread start and stop
+
+    /// <summary>
+    /// Stops CiHandler thread
+    /// </summary>
+    private void StopCiHandlerThread()
+    {
+      if (CiMenuThread != null)
+      {
+        CiMenuThread.Abort();
+        CiMenuThread = null;
+      }
+    }
+
+    /// <summary>
+    /// Starts CiHandler thread
+    /// </summary>
+    private void StartCiHandlerThread()
+    {
+      // Check if the polling thread is still alive. It will be terminated in case of errors, i.e. when CI callback failed.
+      if (CiMenuThread != null && !CiMenuThread.IsAlive)
+      {
+        CiMenuThread.Abort();
+        CiMenuThread = null;
+      }
+      if (CiMenuThread == null)
+      {
+        Log.Log.Debug(FormatMessage("Starting new CI handler thread"));
+        StopThread = false;
+        CiMenuThread = new Thread(new ThreadStart(CiMenuHandler));
+        CiMenuThread.Name = String.Format("{0} CiMenuHandler", _CardName);
+        CiMenuThread.IsBackground = true;
+        CiMenuThread.Priority = ThreadPriority.Lowest;
+        CiMenuThread.Start();
+      }
+    }
+
+    #endregion
+
+    #region CiMenuHandlerThread for polling status and handling MMI
+
+    /// <summary>
+    /// Thread that checks for CI menu 
+    /// </summary>
+    private void CiMenuHandler()
+    {
+      Log.Log.Debug(FormatMessage("CI handler thread start polling status"));
+      try
+      {
+        while (!StopThread)
+        {
+          DD_CAM_MENU_DATA ciMenu;
+          if (GetCamMenuText(out ciMenu))
+          {
+            ProcessCamMenu(ciMenu);
+          }
+          Thread.Sleep(500);
+        };
+      }
+      catch (ThreadAbortException) { }
+      catch (Exception ex)
+      {
+        Log.Log.Debug(FormatMessage("error in CiMenuHandler thread\r\n{0}"), ex.ToString());
+        return;
+      }
+    }
+
+    #endregion
+
+    #region IDisposable members
+
+    /// <summary>
+    /// Disposes COM task memory resources
+    /// </summary>
+    public override void Dispose()
+    {
+      base.Dispose();
+    }
+
+    #endregion
+
+  }
+}
\ No newline at end of file
Index: TVLibrary/TVLibrary/Implementations/DVB/DisEqC/GenericBDAS.cs
===================================================================
--- TVLibrary/TVLibrary/Implementations/DVB/DisEqC/GenericBDAS.cs	(revision 26036)
+++ TVLibrary/TVLibrary/Implementations/DVB/DisEqC/GenericBDAS.cs	(working copy)
@@ -25,13 +25,17 @@
 
 namespace TvLibrary.Implementations.DVB
 {
-  internal class GenericBDAS: IDisposable
+  /// <summary>
+  /// Generic BDA DiSEqC support.
+  /// </summary>
+  public class GenericBDAS: IDisposable
   {
     #region variables
 
     private readonly IKsPropertySet _propertySet;
     protected IBDA_Topology _TunerDevice;
     protected bool _isGenericBDAS;
+    protected String _CardName = "GenericBDAS";
 
     #endregion
 
@@ -60,16 +64,25 @@
           _propertySet.QuerySupported(guidBdaDigitalDemodulator, (int)BdaDigitalModulator.MODULATION_TYPE, out supported);
           if ((supported & KSPropertySupport.Set) != 0)
           {
-            Log.Log.Debug("GenericBDAS: DiSEqC capable card found!");
+            Log.Log.Debug(FormatMessage("DiSEqC capable card found!"));
             _isGenericBDAS = true;
           }
         }
       }
       else
-        Log.Log.Info("GenericBDAS: tuner pin not found!");
+        Log.Log.Info(FormatMessage("tuner pin not found!"));
     }
 
     /// <summary>
+    /// Formats the log message to contain card type name at start.
+    /// </summary>
+    /// <param name="LogMessage"></param>
+    /// <returns></returns>
+    protected String FormatMessage(String LogMessage)
+    {
+      return String.Format("{0}:{1}", _CardName, LogMessage);
+    }
+    /// <summary>
     /// Sends the diseq command.
     /// </summary>
     /// <param name="channel">The channel.</param>
@@ -79,27 +92,27 @@
       switch (channel.DisEqc)
       {
         case DisEqcType.Level1AA:
-          Log.Log.Info("GenericBDAS:  Level1AA - SendDiSEqCCommand(0x00)");
+          Log.Log.Info(FormatMessage("  Level1AA - SendDiSEqCCommand(0x00)"));
           SendDiSEqCCommand(0x00);
           break;
         case DisEqcType.Level1AB:
-          Log.Log.Info("GenericBDAS:  Level1AB - SendDiSEqCCommand(0x01)");
+          Log.Log.Info(FormatMessage("  Level1AB - SendDiSEqCCommand(0x01)"));
           SendDiSEqCCommand(0x01);
           break;
         case DisEqcType.Level1BA:
-          Log.Log.Info("GenericBDAS:  Level1BA - SendDiSEqCCommand(0x0100)");
+          Log.Log.Info(FormatMessage("  Level1BA - SendDiSEqCCommand(0x0100)"));
           SendDiSEqCCommand(0x0100);
           break;
         case DisEqcType.Level1BB:
-          Log.Log.Info("GenericBDAS:  Level1BB - SendDiSEqCCommand(0x0101)");
+          Log.Log.Info(FormatMessage("  Level1BB - SendDiSEqCCommand(0x0101)"));
           SendDiSEqCCommand(0x0101);
           break;
         case DisEqcType.SimpleA:
-          Log.Log.Info("GenericBDAS:  SimpleA - SendDiSEqCCommand(0x00)");
+          Log.Log.Info(FormatMessage("  SimpleA - SendDiSEqCCommand(0x00)"));
           SendDiSEqCCommand(0x00);
           break;
         case DisEqcType.SimpleB:
-          Log.Log.Info("GenericBDAS:  SimpleB - SendDiSEqCCommand(0x01)");
+          Log.Log.Info(FormatMessage("  SimpleB - SendDiSEqCCommand(0x01)"));
           SendDiSEqCCommand(0x01);
           break;
         default:
@@ -114,7 +127,7 @@
     /// <returns>true if succeeded, otherwise false</returns>
     protected bool SendDiSEqCCommand(ulong ulRange)
     {
-      Log.Log.Info("GenericBDAS:  SendDiSEqC Command {0}", ulRange);
+      Log.Log.Info(FormatMessage("  SendDiSEqC Command {0}"), ulRange);
       // get ControlNode of tuner control node
       object ControlNode;
       int hr = _TunerDevice.GetControlNode(0, 1, 0, out ControlNode);
@@ -133,7 +146,7 @@
               if (FrequencyFilter != null)
               {
                 hr = FrequencyFilter.put_Range(ulRange);
-                Log.Log.Info("GenericBDAS:  put_Range:{0} success:{1}", ulRange, hr);
+                Log.Log.Info(FormatMessage("  put_Range:{0} success:{1}"), ulRange, hr);
                 if (hr == 0)
                 {
                   // did it accept the changes? 
@@ -143,26 +156,26 @@
                     hr = DecviceControl.CommitChanges();
                     if (hr == 0)
                     {
-                      Log.Log.Info("GenericBDAS:  CommitChanges() Succeeded");
+                      Log.Log.Info(FormatMessage("  CommitChanges() Succeeded"));
                       return true;
                     }
                     // reset configuration
-                    Log.Log.Info("GenericBDAS:  CommitChanges() Failed!");
+                    Log.Log.Info(FormatMessage("  CommitChanges() Failed!"));
                     DecviceControl.StartChanges();
                     DecviceControl.CommitChanges();
                     return false;
                   }
-                  Log.Log.Info("GenericBDAS:  CheckChanges() Failed!");
+                  Log.Log.Info(FormatMessage("  CheckChanges() Failed!"));
                   return false;
                 }
-                Log.Log.Info("GenericBDAS:  put_Range Failed!");
+                Log.Log.Info(FormatMessage("  put_Range Failed!"));
                 return false;
               }
             }
           }
         }
       }
-      Log.Log.Info("GenericBDAS:  GetControlNode Failed!");
+      Log.Log.Info(FormatMessage("  GetControlNode Failed!"));
       return false;
     }
 
@@ -191,19 +204,19 @@
               if (FrequencyFilter != null)
               {
                 hr = FrequencyFilter.get_Range(out pulRange);
-                Log.Log.Info("GenericBDAS:  get_Range:{0} success:{1}", pulRange, hr);
+                Log.Log.Info(FormatMessage("  get_Range:{0} success:{1}"), pulRange, hr);
                 if (hr == 0)
                 {
                   return true;
                 }
-                Log.Log.Info("GenericBDAS:  get_Range Failed!");
+                Log.Log.Info(FormatMessage("  get_Range Failed!"));
                 return false;
               }
             }
           }
         }
       }
-      Log.Log.Info("GenericBDAS:  GetControlNode Failed!");
+      Log.Log.Info(FormatMessage("  GetControlNode Failed!"));
       pulRange = 0;
       return false;
     }
@@ -232,7 +245,7 @@
 
     #region IDisposable Member
 
-    public void Dispose()
+    public virtual void Dispose()
     {
     }
 
Index: TVLibrary/TVLibrary/Implementations/DVB/Graphs/TvCardDvbBase.cs
===================================================================
--- TVLibrary/TVLibrary/Implementations/DVB/Graphs/TvCardDvbBase.cs	(revision 26036)
+++ TVLibrary/TVLibrary/Implementations/DVB/Graphs/TvCardDvbBase.cs	(working copy)
@@ -168,6 +168,11 @@
     protected IBaseFilter _filterWinTvUsb;
 
     /// <summary>
+    /// DigitalDevices CI filter
+    /// </summary>
+    protected IBaseFilter _filterDigitalDevicesCI;
+
+    /// <summary>
     /// Capture device
     /// </summary>
     protected DsDevice _captureDevice;
@@ -178,6 +183,11 @@
     protected DsDevice _deviceWinTvUsb;
 
     /// <summary>
+    /// DigitalDevices CI device
+    /// </summary>
+    protected DsDevice _deviceDigitalDevicesCI;
+
+    /// <summary>
     /// EPG Grabber callback
     /// </summary>
     protected BaseEpgGrabber _epgGrabberCallback;
@@ -281,7 +291,7 @@
     /// <param name="channel">The channel.</param>
     /// <returns></returns>
     public virtual ITvSubChannel Tune(int subChannelId, IChannel channel)
-    {      
+    {
       bool performTune = (_previousChannel == null || _previousChannel.IsDifferentTransponder(channel));      
       ITvSubChannel ch = SubmitTuneRequest(subChannelId, channel, _tuneRequest, performTune);
       _previousChannel = channel;
@@ -466,11 +476,6 @@
             {
               Log.Log.WriteFile("dvb:SubmitTuneRequest  returns:0x{0:X} - {1}{2}", hr, HResult.GetDXErrorString(hr),
                                 DsError.GetErrorText(hr));
-              //remove subchannel.
-              /*if (newSubChannel)
-              {
-              _mapSubChannels.Remove(subChannelId);
-              }*/
               throw new TvException("Unable to tune to channel");
             }
           }
@@ -1002,6 +1007,106 @@
     }
 
     /// <summary>
+    /// Checks if the DigitalDevices CI module is installed
+    /// if so it adds it to the directshow graph
+    /// in the following way:
+    /// [Network Provider]->[Tuner Filter]->[Capture Filter]->[Digital Devices Common Interface]->[InfTee]
+    /// alternaively like this:
+    /// [Network Provider]->[Tuner Filter]->[Digital Devices Common Interface]->[InfTee]
+    /// </summary>
+    /// <param name="captureFilter">The capture filter.</param>
+    /// <param name="captureDevice">The active DsDevice.</param>
+    /// <returns>
+    /// true if hardware found and graph building succeeded, else false
+    /// </returns>
+    protected bool AddDigitalDevicesCIModule(IBaseFilter captureFilter, DsDevice captureDevice)
+    {
+      FilterInfo pInfo;
+      IBaseFilter tmpCiFilter = null;
+      String CardName = String.Empty;
+      String CiDeviceName = String.Empty;
+      bool filterSuccess = false;
+
+      captureFilter.QueryFilterInfo(out pInfo);
+      //Log.Log.Debug(pInfo.achName);
+
+      if (captureDevice == null)
+        return false;
+
+     if (!captureDevice.DevicePath.ToLowerInvariant().Contains("fbca-11de-b16f-000000004d56"))
+        return false;
+
+      try
+      {
+        DsDevice[] capDevices = DsDevice.GetDevicesOfCat(FilterCategory.BDAReceiverComponentsCategory);
+        DsDevice DDCIDevice = null;
+        for (int capIndex = 0; capIndex < capDevices.Length; capIndex++)
+        {
+          if (capDevices[capIndex].Name != null && DigitalDevices.CIFilterNames.Contains(capDevices[capIndex].Name))
+          {
+            CiDeviceName = capDevices[capIndex].Name;
+            if (!DevicesInUse.Instance.IsUsed(capDevices[capIndex]))
+            {
+              DDCIDevice = capDevices[capIndex];
+              break;
+            }
+          }
+        }
+        if (DDCIDevice == null)
+        {
+          return false; // graph building continued by caller
+        }
+        //DigitalDevices ci module found
+        Log.Log.Info("dvb:  {0} detected", CiDeviceName);
+        Log.Log.Info("dvb:  Adding {0} to graph", CiDeviceName);
+        //add filter to graph
+
+        if (_graphBuilder.AddSourceFilterForMoniker(DDCIDevice.Mon, null, DDCIDevice.Name, out tmpCiFilter) != 0) //cannot add filter to graph...
+        {
+          Log.Log.Info("dvb:  failed to add {0} filter to graph", CiDeviceName);
+          return false;
+        }
+
+        String filterName = tunerOnly ? "Tuner" : "Capture";
+        //now render [Tuner/Capture]->[CI]
+        Log.Log.Info("dvb:  Render [{0}]->[{1}]", filterName, CiDeviceName);
+        if (_capBuilder.RenderStream(null, null, captureFilter, null, tmpCiFilter) != 0)
+        {
+          Log.Log.Info("dvb:  Render [{0}]->[{1}] failed", filterName, CiDeviceName);
+          return false;
+        }
+
+        //Finally render [CI]->[InfTee]
+        Log.Log.Info("dvb:  Render [{0}]->[InfTee-Main]", CiDeviceName);
+        if (_capBuilder.RenderStream(null, null, tmpCiFilter, null, _infTeeMain) != 0)
+        {
+          Log.Log.Error("dvb:  Render [{0}]->[InfTee-Main] failed", CiDeviceName);
+          return false;
+        }
+        _filterDigitalDevicesCI = tmpCiFilter;
+        _deviceDigitalDevicesCI = DDCIDevice;
+        DevicesInUse.Instance.Add(DDCIDevice);
+
+        filterSuccess = true;
+        return filterSuccess;
+      }
+      catch (Exception ex)
+      {
+        Log.Log.Error("dvb:   Error adding CI: {0}", ex.Message);
+        filterSuccess = false;
+        return filterSuccess;
+      }
+      finally
+      {
+        if (!filterSuccess && tmpCiFilter != null)
+        {
+          _graphBuilder.RemoveFilter(tmpCiFilter);
+          Release.ComObject(CiDeviceName, tmpCiFilter);
+        }
+      }
+    }
+
+    /// <summary>
     /// Finds the correct bda tuner/capture filters and adds them to the graph
     /// Creates a graph like
     /// [NetworkProvider]->[Tuner]->[Capture]->[Inftee]->[Demuxer]
@@ -1218,6 +1323,15 @@
         {
           Log.Log.WriteFile("dvb:  Render [Tuner]->[Capture] AOK");
           // render [Capture]->[Inf Tee]
+          if (AddDigitalDevicesCIModule(tmp, devices[i]))
+          {
+            _filterCapture = tmp;
+            _captureDevice = devices[i];
+            DevicesInUse.Instance.Add(devices[i]);
+            Log.Log.WriteFile("dvb:  OK");
+            break;
+          }
+
           if (AddWinTvCIModule(tmp))
           {
             _filterCapture = tmp;
Index: TVLibrary/TVLibrary/TVLibrary.csproj
===================================================================
--- TVLibrary/TVLibrary/TVLibrary.csproj	(revision 25919)
+++ TVLibrary/TVLibrary/TVLibrary.csproj	(working copy)
@@ -97,6 +97,7 @@
     <Compile Include="Implementations\Analog\Graphs\HDPVR\TVCardHDPVR.cs" />
     <Compile Include="Implementations\BaseSubChannel.cs" />
     <Compile Include="Implementations\DVB\ConditionalAccess\ConditionalAccessContext.cs" />
+    <Compile Include="Implementations\DVB\ConditionalAccess\DigitalDevices\DigitalDevices.cs" />
     <Compile Include="Implementations\DVB\ConditionalAccess\ProfRed\ProfRed.cs" />
     <Compile Include="Implementations\DVB\DisEqC\ConexantBDA.cs" />
     <Compile Include="Implementations\DVB\DisEqC\TeVii.cs" />
