+ 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)
+{
+ if (slot->use_count)
+ {
+ eDebug("CI Slot %d: removed... usecount %d", slot->getSlotID(), slot->use_count);
+ for (PMTHandlerList::iterator it(m_pmt_handlers.begin());
+ it != m_pmt_handlers.end(); ++it)
+ {
+ if (it->cislot == slot) // remove the base slot
+ it->cislot = slot->linked_next;
+ else if (it->cislot)
+ {
+ eDVBCISlot *prevSlot = it->cislot, *hSlot = it->cislot->linked_next;
+ while (hSlot)
+ {
+ if (hSlot == slot) {
+ prevSlot->linked_next = slot->linked_next;
+ break;
+ }
+ prevSlot = hSlot;
+ hSlot = hSlot->linked_next;
+ }
+ }
+ }
+ if (slot->linked_next)
+ slot->linked_next->setSource(slot->current_source);
+ else // last CI in chain
+ setInputSource(slot->current_tuner, slot->current_source);
+ slot->linked_next = 0;
+ slot->use_count=0;
+ slot->plugged=true;
+ slot->user_mapped=false;
+ slot->removeService(0xFFFF);
+ recheckPMTHandlers();
+ }
+}
+
+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()
+{
+ eDebugCI("recheckPMTHAndlers()");
+ for (PMTHandlerList::iterator it(m_pmt_handlers.begin());
+ it != m_pmt_handlers.end(); ++it)
+ {
+ CAID_LIST caids;
+ ePtr<eDVBService> service;
+ eServiceReferenceDVB ref;
+ eDVBCISlot *tmp = it->cislot;
+ eDVBServicePMTHandler *pmthandler = it->pmthandler;
+ eDVBServicePMTHandler::program p;
+ bool plugged_cis_exist = false;
+
+ pmthandler->getServiceReference(ref);
+ pmthandler->getService(service);
+
+ eDebugCI("recheck %p %s", pmthandler, ref.toString().c_str());
+ for (eSmartPtrList<eDVBCISlot>::iterator ci_it(m_slots.begin()); ci_it != m_slots.end(); ++ci_it)
+ if (ci_it->plugged && ci_it->getCAManager())
+ {
+ eDebug("Slot %d plugged", ci_it->getSlotID());
+ ci_it->plugged = false;
+ plugged_cis_exist = true;
+ }
+
+ // check if this pmt handler has already assigned CI(s) .. and this CI(s) are already running
+ if (!plugged_cis_exist)
+ {
+ while(tmp)
+ {
+ if (!tmp->running_services.empty())
+ break;
+ tmp=tmp->linked_next;
+ }
+ if (tmp) // we dont like to change tsmux for running services
+ {
+ eDebugCI("already assigned and running CI!\n");
+ continue;
+ }
+ }
+
+ if (!pmthandler->getProgramInfo(p))
+ {
+ int cnt=0;
+ for (caidSet::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)
+ {
+ eDebugCI("check Slot %d", ci_it->getSlotID());
+ bool useThis=false;
+ bool user_mapped=true;
+ eDVBCICAManagerSession *ca_manager = ci_it->getCAManager();
+
+ if (ca_manager)
+ {
+ int mask=0;
+ if (!ci_it->possible_services.empty())
+ {
+ mask |= 1;
+ serviceSet::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;
+ }
+ else // check parent
+ {
+ eServiceReferenceDVB parent_ref = ref.getParentServiceReference();
+ if (parent_ref)
+ {
+ it = ci_it->possible_services.find(ref);
+ if (it != ci_it->possible_services.end())
+ {
+ eDebug("parent '%s' of '%s' is in service list of slot %d... so use it",
+ parent_ref.toString().c_str(), ref.toString().c_str(), ci_it->getSlotID());
+ useThis = true;
+ }
+ }
+ }
+ }
+ if (!useThis && !ci_it->possible_providers.empty())
+ {
+ eDVBNamespace ns = ref.getDVBNamespace();
+ mask |= 2;
+ if (!service) // subservice?
+ {
+ eServiceReferenceDVB parent_ref = ref.getParentServiceReference();
+ eDVBDB::getInstance()->getService(parent_ref, service);
+ }
+ if (service)
+ {
+ providerSet::iterator it = ci_it->possible_providers.find(providerPair(service->m_provider_name, ns.get()));
+ if (it != ci_it->possible_providers.end())
+ {
+ eDebug("'%s/%08x' is in provider list of slot %d... so use it", service->m_provider_name.c_str(), ns.get(), ci_it->getSlotID());
+ useThis = true;
+ }
+ }
+ }
+ if (!useThis && !ci_it->possible_caids.empty())
+ {
+ mask |= 4;
+ for (CAID_LIST::iterator ca(caids.begin()); ca != caids.end(); ++ca)
+ {
+ caidSet::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;
+ user_mapped = false;
+ break;
+ }
+ }
+ }
+ }
+
+ 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...
+ {
+ eDebugCI("already assigned!");
+ continue;
+ }
+ eDebugCI("current slot %d usecount %d", ci_it->getSlotID(), ci_it->use_count);
+ if (ci_it->use_count) // check if this CI can descramble more than one service
+ {
+ bool found = false;
+ useThis = false;
+ PMTHandlerList::iterator tmp = m_pmt_handlers.begin();
+ while (!found && tmp != m_pmt_handlers.end())
+ {
+ eDebugCI(".");
+ eDVBCISlot *tmp_cislot = tmp->cislot;
+ while (!found && tmp_cislot)
+ {
+ eDebugCI("..");
+ eServiceReferenceDVB ref2;
+ tmp->pmthandler->getServiceReference(ref2);
+ if ( tmp_cislot == ci_it && it->pmthandler != tmp->pmthandler )
+ {
+ eDebugCI("check pmthandler %s for same service/tp", ref2.toString().c_str());
+ eDVBChannelID s1, s2;
+ if (ref != ref2)
+ {
+ eDebugCI("different services!");
+ ref.getChannelID(s1);
+ ref2.getChannelID(s2);
+ }
+ if (ref == ref2 || (s1 == s2 && canDescrambleMultipleServices(tmp_cislot->getSlotID())))
+ {
+ found = true;
+ eDebugCI("found!");
+ eDVBCISlot *tmpci = it->cislot = tmp->cislot;
+ while(tmpci)
+ {
+ ++tmpci->use_count;
+ eDebug("(2)CISlot %d, usecount now %d", tmpci->getSlotID(), tmpci->use_count);
+ tmpci=tmpci->linked_next;
+ }
+ }
+ }
+ tmp_cislot=tmp_cislot->linked_next;
+ }
+ eDebugCI("...");
+ ++tmp;
+ }
+ }
+
+ if (useThis)
+ {
+ if (ci_it->user_mapped) // we dont like to link user mapped CIs
+ {
+ eDebugCI("user mapped CI already in use... dont link!");
+ continue;
+ }
+
+ ++ci_it->use_count;
+ eDebug("(1)CISlot %d, usecount now %d", ci_it->getSlotID(), 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;
+ eDebugCI("assigned!");
+ gotPMT(pmthandler);
+ }
+
+ if (it->cislot && user_mapped) // CI assigned to this pmthandler in this run.. and user mapped? then we break here.. we dont like to link other CIs to user mapped CIs
+ {
+ eDebugCI("user mapped CI assigned... dont link CIs!");
+ break;
+ }
+ }
+ }
+ }
+}
+
+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;
+ eDVBCISlot *base_slot = slot;
+ 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)
+ {
+ eDVBCISlot *next = slot->linked_next;
+ if (!sameServiceExist)
+ {
+ 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());
+ }
+
+ if (!--slot->use_count)
+ {
+ if (slot->linked_next)
+ slot->linked_next->setSource(slot->current_source);
+ else
+ setInputSource(slot->current_tuner, slot->current_source);
+
+ if (base_slot != slot)
+ {
+ eDVBCISlot *tmp = it->cislot;
+ while(tmp->linked_next != slot)
+ tmp = tmp->linked_next;
+ ASSERT(tmp);
+ if (slot->linked_next)
+ tmp->linked_next = slot->linked_next;
+ else
+ tmp->linked_next = 0;
+ }
+ else // removed old base slot.. update ptr
+ base_slot = slot->linked_next;
+ slot->linked_next = 0;
+ slot->user_mapped = false;
+ }
+ eDebug("(3) slot %d usecount is now %d", slot->getSlotID(), 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)
+ {
+ eDebugCI("check slot %d %d %d", tmp->getSlotID(), tmp->running_services.empty(), canDescrambleMultipleServices(tmp->getSlotID()));
+ if (tmp->running_services.empty() || canDescrambleMultipleServices(tmp->getSlotID()))
+ tmp->sendCAPMT(pmthandler);
+ tmp = tmp->linked_next;
+ }
+ }
+}
+
+int eDVBCIInterfaces::getMMIState(int slotid)
+{
+ eDVBCISlot *slot;
+
+ if( (slot = getSlot(slotid)) == 0 )
+ return -1;
+
+ return slot->getMMIState();