fix seekable status when service is already running, only show infobar if InfoBarShow...
[enigma2.git] / lib / dvb_ci / dvbci.cpp
index 72dfd388920929fb0a135566960bc6b06220bb30..79f1c0d655b4e5ca79d81e4602541fddb45fd53c 100644 (file)
@@ -1,18 +1,31 @@
 #include <fcntl.h>
+#include <sys/ioctl.h>
 
 #include <lib/base/init.h>
 #include <lib/base/init_num.h>
+#include <lib/base/ebase.h>
 
 #include <lib/base/eerror.h>
+#include <lib/base/nconfig.h> // access to python config
+#include <lib/dvb/db.h>
+#include <lib/dvb/pmt.h>
 #include <lib/dvb_ci/dvbci.h>
 #include <lib/dvb_ci/dvbci_session.h>
-
+#include <lib/dvb_ci/dvbci_camgr.h>
 #include <lib/dvb_ci/dvbci_ui.h>
+#include <lib/dvb_ci/dvbci_appmgr.h>
+#include <lib/dvb_ci/dvbci_mmi.h>
+
+#include <dvbsi++/ca_program_map_section.h>
+
+eDVBCIInterfaces *eDVBCIInterfaces::instance = 0;
 
 eDVBCIInterfaces::eDVBCIInterfaces()
 {
        int num_ci = 0;
-
+       
+       instance = this;
+       
        eDebug("scanning for common interfaces..");
 
        while (1)
@@ -32,6 +45,19 @@ eDVBCIInterfaces::eDVBCIInterfaces()
                ++num_ci;
        }
 
+       if (num_ci > 1) // // FIXME .. we force DM8000 when more than one CI Slot is avail
+       {
+               setInputSource(0, TUNER_A);
+               setInputSource(1, TUNER_B);
+               setInputSource(2, TUNER_C);
+               setInputSource(3, TUNER_D);
+       }
+       else
+       {
+               setInputSource(0, TUNER_A);
+               setInputSource(1, TUNER_B);
+       }
+
        eDebug("done, found %d common interface slots", num_ci);
 }
 
@@ -39,20 +65,749 @@ eDVBCIInterfaces::~eDVBCIInterfaces()
 {
 }
 
+eDVBCIInterfaces *eDVBCIInterfaces::getInstance()
+{
+       return instance;
+}
+
+eDVBCISlot *eDVBCIInterfaces::getSlot(int slotid)
+{
+       for(eSmartPtrList<eDVBCISlot>::iterator i(m_slots.begin()); i != m_slots.end(); ++i)
+               if(i->getSlotID() == slotid)
+                       return i;
+
+       eDebug("FIXME: request for unknown slot");
+                       
+       return 0;
+}
+
+int eDVBCIInterfaces::getSlotState(int slotid)
+{
+       eDVBCISlot *slot;
+
+       if( (slot = getSlot(slotid)) == 0 )
+               return eDVBCISlot::stateInvalid;
+
+       return slot->getState();
+}
+
+int eDVBCIInterfaces::reset(int slotid)
+{
+       eDVBCISlot *slot;
+
+       if( (slot = getSlot(slotid)) == 0 )
+               return -1;
+
+       eDVBCISession::deleteSessions(slot);
+       ciRemoved(slot);
+
+       return slot->reset();
+}
+
+int eDVBCIInterfaces::initialize(int slotid)
+{
+       eDVBCISlot *slot;
+
+       if( (slot = getSlot(slotid)) == 0 )
+               return -1;
+
+       slot->removeService();
+
+       return sendCAPMT(slotid);
+}
+
+int eDVBCIInterfaces::sendCAPMT(int slotid)
+{
+       eDVBCISlot *slot;
+
+       if( (slot = getSlot(slotid)) == 0 )
+               return -1;
+
+       PMTHandlerList::iterator it = m_pmt_handlers.begin();
+       while (it != m_pmt_handlers.end())
+       {
+               eDVBCISlot *tmp = it->cislot;
+               while (tmp && tmp != slot)
+                       tmp = tmp->linked_next;
+               if (tmp)
+               {
+                       tmp->sendCAPMT(it->pmthandler);  // send capmt
+                       break;
+               }
+               ++it;
+       }
+
+       return 0;
+}
+
+int eDVBCIInterfaces::startMMI(int slotid)
+{
+       eDVBCISlot *slot;
+
+       if( (slot = getSlot(slotid)) == 0 )
+               return -1;
+       
+       return slot->startMMI();
+}
+
+int eDVBCIInterfaces::stopMMI(int slotid)
+{
+       eDVBCISlot *slot;
+
+       if( (slot = getSlot(slotid)) == 0 )
+               return -1;
+       
+       return slot->stopMMI();
+}
+
+int eDVBCIInterfaces::answerText(int slotid, int answer)
+{
+       eDVBCISlot *slot;
+
+       if( (slot = getSlot(slotid)) == 0 )
+               return -1;
+       
+       return slot->answerText(answer);
+}
+
+int eDVBCIInterfaces::answerEnq(int slotid, char *value)
+{
+       eDVBCISlot *slot;
+
+       if( (slot = getSlot(slotid)) == 0 )
+               return -1;
+       
+       return slot->answerEnq(value);
+}
+
+int eDVBCIInterfaces::cancelEnq(int slotid)
+{
+       eDVBCISlot *slot;
+
+       if( (slot = getSlot(slotid)) == 0 )
+               return -1;
+       
+       return slot->cancelEnq();
+}
+
+void eDVBCIInterfaces::ciRemoved(eDVBCISlot *slot)
+{
+       for (PMTHandlerList::iterator it(m_pmt_handlers.begin());
+               it != m_pmt_handlers.end(); ++it)
+       {
+               eServiceReferenceDVB ref;
+               it->pmthandler->getServiceReference(ref);
+               slot->removeService(ref.getServiceID().get());
+               if (slot->use_count && !--slot->use_count)
+               {
+                       if (slot->linked_next)
+                               slot->linked_next->setSource(slot->current_source);
+                       else
+                               setInputSource(slot->current_tuner, slot->current_source);
+
+                       if (it->cislot == slot) // remove the base slot
+                               it->cislot = slot->linked_next;
+                       else
+                       {
+                               if (slot->linked_next)
+                               {
+                                       eDVBCISlot *tmp = it->cislot;
+                                       while(tmp->linked_next != slot)
+                                               tmp = tmp->linked_next;
+                                       ASSERT(tmp);
+                                       tmp->linked_next = slot->linked_next;
+                               }
+                       }
+                       slot->linked_next=0;
+               }
+       }
+}
+
+static bool canDescrambleMultipleServices(int slotid)
+{
+       char configStr[255];
+       snprintf(configStr, 255, "config.ci.%d.canDescrambleMultipleServices", slotid);
+       std::string str;
+       ePythonConfigQuery::getConfigValue(configStr, str);
+       if ( str == "auto" )
+       {
+               std::string appname = eDVBCI_UI::getInstance()->getAppName(slotid);
+               if (appname.find("AlphaCrypt") != std::string::npos)
+                       return true;
+       }
+       else if (str == "yes")
+               return true;
+       return false;
+}
+
+void eDVBCIInterfaces::recheckPMTHandlers()
+{
+//     eDebug("recheckPMTHAndlers()");
+       for (PMTHandlerList::iterator it(m_pmt_handlers.begin());
+               it != m_pmt_handlers.end(); ++it)
+       {
+               CAID_LIST caids;
+               ePtr<eDVBService> service;
+               eServiceReferenceDVB ref;
+               eDVBServicePMTHandler *pmthandler = it->pmthandler;
+               eDVBServicePMTHandler::program p;
+
+               pmthandler->getServiceReference(ref);
+               pmthandler->getService(service);
+               if (!pmthandler->getProgramInfo(p))
+               {
+                       int cnt=0;
+                       for (std::set<uint16_t>::reverse_iterator x(p.caids.rbegin()); x != p.caids.rend(); ++x, ++cnt)
+                               caids.push_front(*x);
+                       if (service && cnt)
+                               service->m_ca = caids;
+               }
+
+               if (service)
+                       caids = service->m_ca;
+
+               if (caids.empty())
+                       continue; // unscrambled service
+
+               for (eSmartPtrList<eDVBCISlot>::iterator ci_it(m_slots.begin()); ci_it != m_slots.end(); ++ci_it)
+               {
+                       bool useThis=false;
+                       eDVBCICAManagerSession *ca_manager = ci_it->getCAManager();
+                       if (ca_manager)
+                       {
+                               int mask=0;
+                               if (!ci_it->possible_services.empty())
+                               {
+                                       mask |= 1;
+                                       std::set<eServiceReference>::iterator it = ci_it->possible_services.find(ref);
+                                       if (it != ci_it->possible_services.end())
+                                       {
+                                               eDebug("'%s' is in service list of slot %d... so use it", ref.toString().c_str(), ci_it->getSlotID());
+                                               useThis = true;
+                                       }
+                               }
+                               if (!useThis && !ci_it->possible_providers.empty())
+                               {
+                                       bool parent=false;
+                                       mask |= 2;
+                                       if (!service) // subservice?
+                                       {
+                                               eServiceReferenceDVB parent_ref = ref.getParentServiceReference();
+                                               eDVBDB::getInstance()->getService(parent_ref, service);
+                                               parent=true;
+                                       }
+                                       if (service)
+                                       {
+                                               std::set<std::string>::iterator it = ci_it->possible_providers.find(service->m_provider_name);
+                                               if (it != ci_it->possible_providers.end())
+                                               {
+                                                       eDebug("'%s' is in provider list of slot %d... so use it", service->m_provider_name.c_str(), ci_it->getSlotID());
+                                                       useThis = true;
+                                               }
+                                       }
+                               }
+                               if (!useThis && !ci_it->possible_caids.empty())
+                               {
+                                       for (CAID_LIST::iterator ca(caids.begin()); ca != caids.end(); ++ca)
+                                       {
+                                               std::set<uint16_t>::iterator it = ci_it->possible_caids.find(*ca);
+                                               if (it != ci_it->possible_caids.end())
+                                               {
+                                                       eDebug("caid '%04x' is in caid list of slot %d... so use it", *ca, ci_it->getSlotID());
+                                                       useThis=true;
+                                                       break;
+                                               }
+                                       }
+                               }
+                               if (!useThis && !mask)
+                               {
+                                       const std::vector<uint16_t> &ci_caids = ca_manager->getCAIDs();
+                                       for (CAID_LIST::iterator ca(caids.begin()); ca != caids.end(); ++ca)
+                                       {
+                                               std::vector<uint16_t>::const_iterator z =
+                                                       std::lower_bound(ci_caids.begin(), ci_caids.end(), *ca);
+                                               if ( z != ci_caids.end() && *z == *ca )
+                                               {
+                                                       eDebug("The CI in Slot %d has said it can handle caid %04x... so use it", ci_it->getSlotID(), *z);
+                                                       useThis=true;
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+
+                       if (useThis)
+                       {
+                               if (ci_it->use_count)  // check if this CI can descramble more than one service
+                               {
+                                       useThis = false;
+                                       PMTHandlerList::iterator tmp = m_pmt_handlers.begin();
+                                       while (tmp != m_pmt_handlers.end())
+                                       {
+                                               if ( tmp->cislot == ci_it && it != tmp )
+                                               {
+                                                       eServiceReferenceDVB ref2;
+                                                       tmp->pmthandler->getServiceReference(ref2);
+                                                       eDVBChannelID s1, s2;
+                                                       if (ref != ref2)
+                                                       {
+                                                               ref.getChannelID(s1);
+                                                               ref2.getChannelID(s2);
+                                                       }
+                                                       if (ref == ref2 || (s1 == s2 && canDescrambleMultipleServices(ci_it->getSlotID())))
+                                                       {
+                                                               useThis = true;
+                                                               break;
+                                                       }
+                                               }
+                                               ++tmp;
+                                       }
+                               }
+                               if (useThis)
+                               {
+                                       // check if this CI is already assigned to this pmthandler
+                                       eDVBCISlot *tmp = it->cislot;
+                                       while(tmp)
+                                       {
+                                               if (tmp == ci_it)
+                                                       break;
+                                               tmp=tmp->linked_next;
+                                       }
+
+                                       if (tmp) // ignore already assigned cislots...
+                                               continue;
+
+                                       ++ci_it->use_count;
+//                                     eDebug("usecount now %d", ci_it->use_count);
+
+                                       data_source ci_source=CI_A;
+                                       switch(ci_it->getSlotID())
+                                       {
+                                               case 0: ci_source = CI_A; break;
+                                               case 1: ci_source = CI_B; break;
+                                               case 2: ci_source = CI_C; break;
+                                               case 3: ci_source = CI_D; break;
+                                               default:
+                                                       eDebug("try to get source for CI %d!!\n", ci_it->getSlotID());
+                                                       break;
+                                       }
+
+                                       if (!it->cislot)
+                                       {
+                                               int tunernum = -1;
+                                               eUsePtr<iDVBChannel> channel;
+                                               if (!pmthandler->getChannel(channel))
+                                               {
+                                                       ePtr<iDVBFrontend> frontend;
+                                                       if (!channel->getFrontend(frontend))
+                                                       {
+                                                               eDVBFrontend *fe = (eDVBFrontend*) &(*frontend);
+                                                               tunernum = fe->getSlotID();
+                                                       }
+                                               }
+                                               ASSERT(tunernum != -1);
+                                               data_source tuner_source = TUNER_A;
+                                               switch (tunernum)
+                                               {
+                                                       case 0: tuner_source = TUNER_A; break;
+                                                       case 1: tuner_source = TUNER_B; break;
+                                                       case 2: tuner_source = TUNER_C; break;
+                                                       case 3: tuner_source = TUNER_D; break;
+                                                       default:
+                                                               eDebug("try to get source for tuner %d!!\n", tunernum);
+                                                               break;
+                                               }
+                                               ci_it->current_tuner = tunernum;
+                                               setInputSource(tunernum, ci_source);
+                                               ci_it->setSource(tuner_source);
+                                       }
+                                       else
+                                       {
+                                               ci_it->current_tuner = it->cislot->current_tuner;
+                                               ci_it->linked_next = it->cislot;
+                                               ci_it->setSource(ci_it->linked_next->current_source);
+                                               ci_it->linked_next->setSource(ci_source);
+                                       }
+                                       it->cislot = ci_it;
+                                       gotPMT(pmthandler);
+                               }
+                       }
+               }
+       }
+}
+
+void eDVBCIInterfaces::addPMTHandler(eDVBServicePMTHandler *pmthandler)
+{
+       // check if this pmthandler is already registered
+       PMTHandlerList::iterator it = m_pmt_handlers.begin();
+       while (it != m_pmt_handlers.end())
+       {
+               if ( *it++ == pmthandler )
+                       return;
+       }
+
+       eServiceReferenceDVB ref;
+       pmthandler->getServiceReference(ref);
+       eDebug("[eDVBCIInterfaces] addPMTHandler %s", ref.toString().c_str());
+
+       m_pmt_handlers.push_back(CIPmtHandler(pmthandler));
+       recheckPMTHandlers();
+}
+
+void eDVBCIInterfaces::removePMTHandler(eDVBServicePMTHandler *pmthandler)
+{
+       PMTHandlerList::iterator it=std::find(m_pmt_handlers.begin(),m_pmt_handlers.end(),pmthandler);
+       if (it != m_pmt_handlers.end())
+       {
+               eDVBCISlot *slot = it->cislot;
+               eDVBServicePMTHandler *pmthandler = it->pmthandler;
+               m_pmt_handlers.erase(it);
+
+               eServiceReferenceDVB service_to_remove;
+               pmthandler->getServiceReference(service_to_remove);
+
+               bool sameServiceExist=false;
+               for (PMTHandlerList::iterator i=m_pmt_handlers.begin(); i != m_pmt_handlers.end(); ++i)
+               {
+                       if (i->cislot)
+                       {
+                               eServiceReferenceDVB ref;
+                               i->pmthandler->getServiceReference(ref);
+                               if ( ref == service_to_remove )
+                               {
+                                       sameServiceExist=true;
+                                       break;
+                               }
+                       }
+               }
+
+               while(slot)
+               {
+                       if (!sameServiceExist)
+                       {
+                               if (slot->getNumOfServices() > 1)
+                               {
+                                       eDebug("[eDVBCIInterfaces] remove last pmt handler for service %s send empty capmt",
+                                               service_to_remove.toString().c_str());
+                                               std::vector<uint16_t> caids;
+                                       caids.push_back(0xFFFF);
+                                       slot->sendCAPMT(pmthandler, caids);  // send a capmt without caids to remove a running service
+                               }
+                               slot->removeService(service_to_remove.getServiceID().get());
+                       }
+
+                       eDVBCISlot *next = slot->linked_next;
+                       if (!--slot->use_count)
+                       {
+                               if (slot->linked_next)
+                                       slot->linked_next->setSource(slot->current_source);
+                               else
+                                       setInputSource(slot->current_tuner, slot->current_source);
+
+                               if (it->cislot == slot) // remove the base slot
+                                       it->cislot = slot->linked_next;
+                               else
+                               {
+                                       if (slot->linked_next)
+                                       {
+                                               eDVBCISlot *tmp = it->cislot;
+                                               while(tmp->linked_next != slot)
+                                                       tmp = tmp->linked_next;
+                                               ASSERT(tmp);
+                                               tmp->linked_next = slot->linked_next;
+                                       }
+                               }
+                               slot->linked_next=0;
+                       }
+//                     eDebug("use_count is now %d", slot->use_count);
+                       slot = next;
+               }
+       }
+       // check if another service is waiting for the CI
+       recheckPMTHandlers();
+}
+
+void eDVBCIInterfaces::gotPMT(eDVBServicePMTHandler *pmthandler)
+{
+       eDebug("[eDVBCIInterfaces] gotPMT");
+       PMTHandlerList::iterator it=std::find(m_pmt_handlers.begin(), m_pmt_handlers.end(), pmthandler);
+       if (it != m_pmt_handlers.end() && it->cislot)
+       {
+               eDVBCISlot *tmp = it->cislot;
+               while(tmp)
+               {
+                       tmp->sendCAPMT(pmthandler);
+                       tmp = tmp->linked_next;
+               }
+       }
+}
+
+int eDVBCIInterfaces::getMMIState(int slotid)
+{
+       eDVBCISlot *slot;
+
+       if( (slot = getSlot(slotid)) == 0 )
+               return -1;
+       
+       return slot->getMMIState();
+}
+
+int eDVBCIInterfaces::setInputSource(int tuner_no, data_source source)
+{
+//     eDebug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+//     eDebug("eDVBCIInterfaces::setInputSource(%d %d)", tuner_no, (int)source);
+       if (getNumOfSlots() > 1) // FIXME .. we force DM8000 when more than one CI Slot is avail
+       {
+               char buf[64];
+               snprintf(buf, 64, "/proc/stb/tsmux/input%d", tuner_no);
+
+               FILE *input=0;
+               if((input = fopen(buf, "wb")) == NULL) {
+                       eDebug("cannot open %s", buf);
+                       return 0;
+               }
+
+               if (tuner_no > 3)
+                       eDebug("setInputSource(%d, %d) failed... dm8000 just have four inputs", tuner_no, (int)source);
+
+               switch(source)
+               {
+                       case CI_A:
+                               fprintf(input, "CI0");
+                               break;
+                       case CI_B:
+                               fprintf(input, "CI1");
+                               break;
+                       case CI_C:
+                               fprintf(input, "CI2");
+                       break;
+                       case CI_D:
+                               fprintf(input, "CI3");
+                               break;
+                       case TUNER_A:
+                               fprintf(input, "A");
+                               break;
+                       case TUNER_B:
+                               fprintf(input, "B");
+                               break;
+                       case TUNER_C:
+                               fprintf(input, "C");
+                               break;
+                       case TUNER_D:
+                               fprintf(input, "D");
+                               break;
+                       default:
+                               eDebug("setInputSource for input %d failed!!!\n", (int)source);
+                               break;
+               }
+
+               fclose(input);
+       }
+       else  // DM7025
+       {
+               char buf[64];
+               snprintf(buf, 64, "/proc/stb/tsmux/input%d", tuner_no);
+
+               if (tuner_no > 1)
+                       eDebug("setInputSource(%d, %d) failed... dm7025 just have two inputs", tuner_no, (int)source);
+
+               FILE *input=0;
+               if((input = fopen(buf, "wb")) == NULL) {
+                       eDebug("cannot open %s", buf);
+                       return 0;
+               }
+
+               switch(source)
+               {
+                       case CI_A:
+                               fprintf(input, "CI");
+                               break;
+                       case TUNER_A:
+                               fprintf(input, "A");
+                               break;
+                       case TUNER_B:
+                               fprintf(input, "B");
+                               break;
+                       default:
+                               eDebug("setInputSource for input %d failed!!!\n", (int)source);
+                               break;
+               }
+
+               fclose(input);
+       }
+       eDebug("eDVBCIInterfaces->setInputSource(%d, %d)", tuner_no, (int)source);
+       return 0;
+}
+
+PyObject *eDVBCIInterfaces::getDescrambleRules(int slotid)
+{
+       eDVBCISlot *slot = getSlot(slotid);
+       if (!slot)
+       {
+               char tmp[255];
+               snprintf(tmp, 255, "eDVBCIInterfaces::getDescrambleRules try to get rules for CI Slot %d... but just %d slots are available", slotid, m_slots.size());
+               PyErr_SetString(PyExc_StandardError, tmp);
+               return 0;
+       }
+       ePyObject tuple = PyTuple_New(3);
+       int caids = slot->possible_caids.size();
+       int services = slot->possible_services.size();
+       int providers = slot->possible_providers.size();
+       ePyObject caid_list = PyList_New(caids);
+       ePyObject service_list = PyList_New(services);
+       ePyObject provider_list = PyList_New(providers);
+       std::set<uint16_t>::iterator caid_it(slot->possible_caids.begin());
+       while(caids)
+       {
+               --caids;
+               PyTuple_SET_ITEM(caid_list, caids, PyLong_FromLong(*caid_it));
+               ++caid_it;
+       }
+       std::set<eServiceReference>::iterator ref_it(slot->possible_services.begin());
+       while(services)
+       {
+               --services;
+               PyTuple_SET_ITEM(service_list, services, PyString_FromString(ref_it->toString().c_str()));
+               ++ref_it;
+       }
+       std::set<std::string>::iterator provider_it(slot->possible_providers.begin());
+       while(providers)
+       {
+               --providers;
+               PyTuple_SET_ITEM(provider_list, providers, PyString_FromString(provider_it->c_str()));
+               ++provider_it;
+       }
+       PyTuple_SET_ITEM(tuple, 0, service_list);
+       PyTuple_SET_ITEM(tuple, 1, provider_list);
+       PyTuple_SET_ITEM(tuple, 2, caid_list);
+       return tuple;
+}
+
+const char *PyObject_TypeStr(PyObject *o)
+{
+       return o->ob_type && o->ob_type->tp_name ? o->ob_type->tp_name : "unknown object type";
+}
+
+RESULT eDVBCIInterfaces::setDescrambleRules(int slotid, SWIG_PYOBJECT(ePyObject) obj )
+{
+       eDVBCISlot *slot = getSlot(slotid);
+       if (!slot)
+       {
+               char tmp[255];
+               snprintf(tmp, 255, "eDVBCIInterfaces::setDescrambleRules try to set rules for CI Slot %d... but just %d slots are available", slotid, m_slots.size());
+               PyErr_SetString(PyExc_StandardError, tmp);
+               return -1;
+       }
+       if (!PyTuple_Check(obj))
+       {
+               char tmp[255];
+               snprintf(tmp, 255, "2nd argument of setDescrambleRules is not a tuple.. it is a '%s'!!", PyObject_TypeStr(obj));
+               PyErr_SetString(PyExc_StandardError, tmp);
+               return -1;
+       }
+       if (PyTuple_Size(obj) != 3)
+       {
+               const char *errstr = "eDVBCIInterfaces::setDescrambleRules not enough entrys in argument tuple!!\n"
+                       "first argument should be a pythonlist with possible services\n"
+                       "second argument should be a pythonlist with possible providers\n"
+                       "third argument should be a pythonlist with possible caids";
+               PyErr_SetString(PyExc_StandardError, errstr);
+               return -1;
+       }
+       ePyObject service_list = PyTuple_GET_ITEM(obj, 0);
+       ePyObject provider_list = PyTuple_GET_ITEM(obj, 1);
+       ePyObject caid_list = PyTuple_GET_ITEM(obj, 2);
+       if (!PyList_Check(service_list) || !PyList_Check(provider_list) || !PyList_Check(caid_list))
+       {
+               char errstr[512];
+               snprintf(errstr, 512, "eDVBCIInterfaces::setDescrambleRules incorrect data types in argument tuple!!\n"
+                       "first argument(%s) should be a pythonlist with possible services (reference strings)\n"
+                       "second argument(%s) should be a pythonlist with possible providers (providername strings)\n"
+                       "third argument(%s) should be a pythonlist with possible caids (ints)",
+                       PyObject_TypeStr(service_list), PyObject_TypeStr(provider_list), PyObject_TypeStr(caid_list));
+               PyErr_SetString(PyExc_StandardError, errstr);
+               return -1;
+       }
+       slot->possible_caids.clear();
+       slot->possible_services.clear();
+       slot->possible_providers.clear();
+       int size = PyList_Size(service_list);
+       while(size)
+       {
+               --size;
+               ePyObject refstr = PyList_GET_ITEM(service_list, size);
+               if (!PyString_Check(refstr))
+               {
+                       char buf[255];
+                       snprintf(buf, 255, "eDVBCIInterfaces::setDescrambleRules entry in service list is not a string.. it is '%s'!!", PyObject_TypeStr(refstr));
+                       PyErr_SetString(PyExc_StandardError, buf);
+                       return -1;
+               }
+               char *tmpstr = PyString_AS_STRING(refstr);
+               eServiceReference ref(tmpstr);
+               if (ref.valid())
+                       slot->possible_services.insert(ref);
+               else
+                       eDebug("eDVBCIInterfaces::setDescrambleRules '%s' is not a valid service reference... ignore!!", tmpstr);
+       };
+       size = PyList_Size(provider_list);
+       while(size)
+       {
+               --size;
+               ePyObject str = PyList_GET_ITEM(provider_list, size);
+               if (!PyString_Check(str))
+               {
+                       char buf[255];
+                       snprintf(buf, 255, "eDVBCIInterfaces::setDescrambleRules entry in provider list is not a string it is '%s'!!", PyObject_TypeStr(str));
+                       PyErr_SetString(PyExc_StandardError, buf);
+                       return -1;
+               }
+               char *tmpstr = PyString_AS_STRING(str);
+               if (strlen(tmpstr))
+                       slot->possible_providers.insert(tmpstr);
+               else
+                       eDebug("eDVBCIInterfaces::setDescrambleRules ignore invalid entry in provider name list!!");
+       };
+       size = PyList_Size(caid_list);
+       while(size)
+       {
+               --size;
+               ePyObject caid = PyList_GET_ITEM(caid_list, size);
+               if (!PyInt_Check(caid))
+               {
+                       char buf[255];
+                       snprintf(buf, 255, "eDVBCIInterfaces::setDescrambleRules entry in caid list is not a long it is '%s'!!", PyObject_TypeStr(caid));
+                       PyErr_SetString(PyExc_StandardError, buf);
+                       return -1;
+               }
+               int tmpcaid = PyInt_AsLong(caid);
+               if (tmpcaid > 0 && tmpcaid < 0x10000)
+                       slot->possible_caids.insert(tmpcaid);
+               else
+                       eDebug("eDVBCIInterfaces::setDescrambleRules %d is not a valid caid... ignore!!", tmpcaid);
+       };
+       return 0;
+}
+
 int eDVBCISlot::send(const unsigned char *data, size_t len)
 {
-       int res;
+       int res=0;
        //int i;
-       //printf("< ");
+       //eDebugNoNewLine("< ");
        //for(i=0;i<len;i++)
-       //      printf("%02x ",data[i]);
-       //printf("\n");
+       //      eDebugNoNewLine("%02x ",data[i]);
+       //eDebug("");
 
-       res = ::write(fd, data, len);
+       if (sendqueue.empty())
+               res = ::write(fd, data, len);
 
-       //printf("write() %d\n",res);
-
-       notifier->setRequested(eSocketNotifier::Read | eSocketNotifier::Priority | eSocketNotifier::Write);
+       if (res < 0 || (unsigned int)res != len)
+       {
+               unsigned char *d = new unsigned char[len];
+               memcpy(d, data, len);
+               sendqueue.push( queueData(d, len) );
+               notifier->setRequested(eSocketNotifier::Read | eSocketNotifier::Priority | eSocketNotifier::Write);
+       }
 
        return res;
 }
@@ -62,45 +817,58 @@ void eDVBCISlot::data(int what)
        if(what == eSocketNotifier::Priority) {
                if(state != stateRemoved) {
                        state = stateRemoved;
-                       printf("ci removed\n");
+                       eDebug("ci removed");
+                       while(sendqueue.size())
+                       {
+                               delete [] sendqueue.top().data;
+                               sendqueue.pop();
+                       }
+                       eDVBCIInterfaces::getInstance()->ciRemoved(this);
+                       eDVBCISession::deleteSessions(this);
                        notifier->setRequested(eSocketNotifier::Read);
-                       //HACK
-                       eDVBCI_UI::getInstance()->setState(0,0);
+                       eDVBCI_UI::getInstance()->setState(getSlotID(),0);
                }
                return;
        }
 
-
-       __u8 data[4096];
-       int r;
-       r = ::read(fd, data, 4096);
+       if (state == stateInvalid)
+               reset();
 
        if(state != stateInserted) {
-               state = stateInserted;
                eDebug("ci inserted");
-
-               //HACK
-               eDVBCI_UI::getInstance()->setState(0,1);
-
+               state = stateInserted;
+               eDVBCI_UI::getInstance()->setState(getSlotID(),1);
+               notifier->setRequested(eSocketNotifier::Read|eSocketNotifier::Priority);
                /* enable PRI to detect removal or errors */
-               notifier->setRequested(eSocketNotifier::Read|eSocketNotifier::Priority|eSocketNotifier::Write);
        }
 
-       if(r > 0) {
-               //int i;
-               //printf("> ");
-               //for(i=0;i<r;i++)
-               //      printf("%02x ",data[i]);
-               //printf("\n");
-               eDVBCISession::receiveData(this, data, r);
-               notifier->setRequested(eSocketNotifier::Read|eSocketNotifier::Priority|eSocketNotifier::Write);
-               return;
+       if (what & eSocketNotifier::Read) {
+               __u8 data[4096];
+               int r;
+               r = ::read(fd, data, 4096);
+               if(r > 0) {
+//                     int i;
+//                     eDebugNoNewLine("> ");
+//                     for(i=0;i<r;i++)
+//                             eDebugNoNewLine("%02x ",data[i]);
+//                     eDebug("");
+                       eDVBCISession::receiveData(this, data, r);
+                       eDVBCISession::pollAll();
+                       return;
+               }
        }
-
-       if(what == eSocketNotifier::Write) {
-               if(eDVBCISession::pollAll() == 0) {
-                       notifier->setRequested(eSocketNotifier::Read | eSocketNotifier::Priority);
+       else if (what & eSocketNotifier::Write) {
+               if (!sendqueue.empty()) {
+                       const queueData &qe = sendqueue.top();
+                       int res = ::write(fd, qe.data, qe.len);
+                       if (res >= 0 && (unsigned int)res == qe.len)
+                       {
+                               delete [] qe.data;
+                               sendqueue.pop();
+                       }
                }
+               else
+                       notifier->setRequested(eSocketNotifier::Read|eSocketNotifier::Priority);
        }
 }
 
@@ -110,24 +878,294 @@ eDVBCISlot::eDVBCISlot(eMainloop *context, int nr)
 {
        char filename[128];
 
+       application_manager = 0;
+       mmi_session = 0;
+       ca_manager = 0;
+       use_count = 0;
+       linked_next = 0;
+       
+       slotid = nr;
+
        sprintf(filename, "/dev/ci%d", nr);
 
        fd = ::open(filename, O_RDWR | O_NONBLOCK);
 
        eDebug("eDVBCISlot has fd %d", fd);
+       state = stateInvalid;
 
        if (fd >= 0)
        {
-               notifier = new eSocketNotifier(context, fd, eSocketNotifier::Read | eSocketNotifier::Priority);
+               notifier = new eSocketNotifier(context, fd, eSocketNotifier::Read | eSocketNotifier::Priority | eSocketNotifier::Write);
                CONNECT(notifier->activated, eDVBCISlot::data);
        } else
        {
                perror(filename);
        }
+       setSource(TUNER_A);
 }
 
 eDVBCISlot::~eDVBCISlot()
 {
 }
 
+void eDVBCISlot::setAppManager( eDVBCIApplicationManagerSession *session )
+{
+       application_manager=session;
+}
+
+void eDVBCISlot::setMMIManager( eDVBCIMMISession *session )
+{
+       mmi_session = session;
+}
+
+void eDVBCISlot::setCAManager( eDVBCICAManagerSession *session )
+{
+       ca_manager = session;
+}
+
+int eDVBCISlot::getSlotID()
+{
+       return slotid;
+}
+
+int eDVBCISlot::reset()
+{
+       eDebug("edvbcislot: reset requested");
+
+       if (state == stateInvalid)
+       {
+               unsigned char buf[256];
+               eDebug("ci flush");
+               while(::read(fd, buf, 256)>0);
+               state = stateResetted;
+       }
+
+       while(sendqueue.size())
+       {
+               delete [] sendqueue.top().data;
+               sendqueue.pop();
+       }
+
+       ioctl(fd, 0);
+
+       return 0;
+}
+
+int eDVBCISlot::startMMI()
+{
+       eDebug("edvbcislot: startMMI()");
+       
+       if(application_manager)
+               application_manager->startMMI();
+       
+       return 0;
+}
+
+int eDVBCISlot::stopMMI()
+{
+       eDebug("edvbcislot: stopMMI()");
+
+       if(mmi_session)
+               mmi_session->stopMMI();
+       
+       return 0;
+}
+
+int eDVBCISlot::answerText(int answer)
+{
+       eDebug("edvbcislot: answerText(%d)", answer);
+
+       if(mmi_session)
+               mmi_session->answerText(answer);
+
+       return 0;
+}
+
+int eDVBCISlot::getMMIState()
+{
+       if(mmi_session)
+               return 1;
+
+       return 0;
+}
+
+int eDVBCISlot::answerEnq(char *value)
+{
+       eDebug("edvbcislot: answerENQ(%s)", value);
+
+       if(mmi_session)
+               mmi_session->answerEnq(value);
+
+       return 0;
+}
+
+int eDVBCISlot::cancelEnq()
+{
+       eDebug("edvbcislot: cancelENQ");
+
+       if(mmi_session)
+               mmi_session->cancelEnq();
+
+       return 0;
+}
+
+int eDVBCISlot::sendCAPMT(eDVBServicePMTHandler *pmthandler, const std::vector<uint16_t> &ids)
+{
+       if (!ca_manager)
+       {
+               eDebug("no ca_manager (no CI plugged?)");
+               return -1;
+       }
+       const std::vector<uint16_t> &caids = ids.empty() ? ca_manager->getCAIDs() : ids;
+       ePtr<eTable<ProgramMapSection> > ptr;
+       if (pmthandler->getPMT(ptr))
+               return -1;
+       else
+       {
+               eDVBTableSpec table_spec;
+               ptr->getSpec(table_spec);
+               int pmt_version = table_spec.version & 0x1F; // just 5 bits
+
+               eServiceReferenceDVB ref;
+               pmthandler->getServiceReference(ref);
+               uint16_t program_number = ref.getServiceID().get();
+               std::map<uint16_t, uint8_t>::iterator it =
+                       running_services.find(program_number);
+
+               if ( it != running_services.end() &&
+                       (pmt_version == it->second) &&
+                       !(caids.size() == 1 && caids[0] == 0xFFFF) )
+               {
+                       eDebug("[eDVBCISlot] dont send self capmt version twice");
+                       return -1;
+               }
+
+               std::vector<ProgramMapSection*>::const_iterator i=ptr->getSections().begin();
+               if ( i == ptr->getSections().end() )
+                       return -1;
+               else
+               {
+                       unsigned char raw_data[2048];
+
+//                     eDebug("send %s capmt for service %04x to slot %d",
+//                             it != running_services.end() ? "UPDATE" : running_services.empty() ? "ONLY" : "ADD",
+//                             program_number, slotid);
+
+                       CaProgramMapSection capmt(*i++,
+                               it != running_services.end() ? 0x05 /*update*/ : running_services.empty() ? 0x03 /*only*/ : 0x04 /*add*/, 0x01, caids );
+                       while( i != ptr->getSections().end() )
+                       {
+               //                      eDebug("append");
+                               capmt.append(*i++);
+                       }
+                       capmt.writeToBuffer(raw_data);
+#if 1
+// begin calc capmt length
+                       int wp=0;
+                       int hlen;
+                       if ( raw_data[3] & 0x80 )
+                       {
+                               int i=0;
+                               int lenbytes = raw_data[3] & ~0x80;
+                               while(i < lenbytes)
+                                       wp = (wp << 8) | raw_data[4 + i++];
+                               wp+=4;
+                               wp+=lenbytes;
+                               hlen = 4 + lenbytes;
+                       }
+                       else
+                       {
+                               wp = raw_data[3];
+                               wp+=4;
+                               hlen = 4;
+                       }
+// end calc capmt length
+//                     eDebug("ca_manager %p dump capmt:", ca_manager);
+//                     for(int i=0;i<wp;i++)
+//                             eDebugNoNewLine("%02x ", raw_data[i]);
+//                     eDebug("");
+#endif
+                       if (caids.size() == 1 && caids[0] == 0xFFFF)
+                       {
+//                             eDebugNoNewLine("SEND EMPTY CAPMT.. old version is %02x", raw_data[hlen+3]);
+                               raw_data[hlen+3] &= ~0x3E;
+                               raw_data[hlen+3] |= ((pmt_version+1) & 0x1F) << 1;
+//                             eDebug(" new version is %02x", raw_data[hlen+3]);
+                       }
+
+                       //dont need tag and lenfield
+                       ca_manager->sendCAPMT(raw_data + hlen, wp - hlen);
+                       running_services[program_number] = pmt_version;
+               }
+       }
+       return 0;
+}
+
+void eDVBCISlot::removeService(uint16_t program_number)
+{
+       if (program_number == 0xFFFF)
+               running_services.clear();  // remove all
+       else
+               running_services.erase(program_number);  // remove single service
+}
+
+int eDVBCISlot::setSource(data_source source)
+{
+       current_source = source;
+       if (eDVBCIInterfaces::getInstance()->getNumOfSlots() > 1) // FIXME .. we force DM8000 when more than one CI Slot is avail
+       {
+               char buf[64];
+               snprintf(buf, 64, "/proc/stb/tsmux/ci%d_input", slotid);
+               FILE *ci = fopen(buf, "wb");
+               switch(source)
+               {
+                       case CI_A:
+                               fprintf(ci, "CI0");
+                               break;
+                       case CI_B:
+                               fprintf(ci, "CI1");
+                               break;
+                       case CI_C:
+                               fprintf(ci, "CI2");
+                               break;
+                       case CI_D:
+                               fprintf(ci, "CI3");
+                               break;
+                       case TUNER_A:
+                               fprintf(ci, "A");
+                               break;
+                       case TUNER_B:
+                               fprintf(ci, "B");
+                               break;
+                       case TUNER_C:
+                               fprintf(ci, "C");
+                               break;
+                               case TUNER_D:
+                               fprintf(ci, "D");
+                               break;
+                       default:
+                               eDebug("setSource %d failed!!!\n", (int)source);
+                               break;
+               }
+               fclose(ci);
+       }
+       else // DM7025
+       {
+//             eDebug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+//             eDebug("eDVBCISlot::enableTS(%d %d)", enable, (int)source);
+               FILE *ci = fopen("/proc/stb/tsmux/input2", "wb");
+               if(ci == NULL) {
+                       eDebug("cannot open /proc/stb/tsmux/input2");
+                       return 0;
+               }
+               if (source != TUNER_A && source != TUNER_B)
+                       eDebug("setSource %d failed!!!\n", (int)source);
+               else
+                       fprintf(ci, "%s", source==TUNER_A ? "A" : "B");  // configure CI data source (TunerA, TunerB)
+               fclose(ci);
+       }
+       eDebug("eDVBCISlot->setSource(%d)", (int)source);
+       return 0;
+}
+
 eAutoInitP0<eDVBCIInterfaces> init_eDVBCIInterfaces(eAutoInitNumbers::dvb, "CI Slots");