InfoBarGenerics.py: fix handling for unused key indication when two times the same...
[enigma2.git] / lib / dvb_ci / dvbci.cpp
index d088075c4544509bdbace8164cf47e01e434c2cc..883fb5a47fd8373b2ce6b7589e34ca91588c837d 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <dvbsi++/ca_program_map_section.h>
 
-#undef CIDEBUG
+//#define CIDEBUG 1
 
 #ifdef CIDEBUG
        #define eDebugCI(x...) eDebug(x)
@@ -200,43 +200,39 @@ int eDVBCIInterfaces::cancelEnq(int slotid)
 
 void eDVBCIInterfaces::ciRemoved(eDVBCISlot *slot)
 {
-       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 (slot->use_count)
        {
-               eServiceReferenceDVB ref;
-               it->pmthandler->getServiceReference(ref);
-               eDebugCI("check %s cislot %p %d\n", ref.toString().c_str(), it->cislot, it->cislot?it->cislot->getSlotID() : -1);
-               slot->removeService(ref.getServiceID().get());
-               if (slot->use_count && !--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 (slot->linked_next)
-                               slot->linked_next->setSource(slot->current_source);
-                       else // last CI in chain
-                               setInputSource(slot->current_tuner, slot->current_source);
-
                        if (it->cislot == slot) // remove the base slot
-                       {
                                it->cislot = slot->linked_next;
-                               eDebugCI("base removed.. so slot is now %p", it->cislot);
-                       }
-                       else
+                       else if (it->cislot)
                        {
-                               eDebugCI("not base removed.. %d", it->cislot->getSlotID());
-                               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;
+                               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;
+                               }
                        }
-                       slot->linked_next=0;
-                       slot->user_mapped=false;
                }
+               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();
        }
-       recheckPMTHandlers();
 }
 
 static bool canDescrambleMultipleServices(int slotid)
@@ -268,23 +264,34 @@ void eDVBCIInterfaces::recheckPMTHandlers()
                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
-               while(tmp)
+               if (!plugged_cis_exist)
                {
-                       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;
+                       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))
@@ -321,6 +328,20 @@ void eDVBCIInterfaces::recheckPMTHandlers()
                                                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())
                                {
@@ -343,6 +364,7 @@ void eDVBCIInterfaces::recheckPMTHandlers()
                                }
                                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);
@@ -530,6 +552,7 @@ void eDVBCIInterfaces::removePMTHandler(eDVBServicePMTHandler *pmthandler)
        if (it != m_pmt_handlers.end())
        {
                eDVBCISlot *slot = it->cislot;
+               eDVBCISlot *base_slot = slot;
                eDVBServicePMTHandler *pmthandler = it->pmthandler;
                m_pmt_handlers.erase(it);
 
@@ -553,20 +576,17 @@ void eDVBCIInterfaces::removePMTHandler(eDVBServicePMTHandler *pmthandler)
 
                while(slot)
                {
+                       eDVBCISlot *next = slot->linked_next;
                        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
-                               }
+                               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)
@@ -574,9 +594,7 @@ void eDVBCIInterfaces::removePMTHandler(eDVBServicePMTHandler *pmthandler)
                                else
                                        setInputSource(slot->current_tuner, slot->current_source);
 
-                               if (it->cislot == slot) // remove the base slot
-                                       it->cislot = slot->linked_next;
-                               else
+                               if (base_slot != slot)
                                {
                                        eDVBCISlot *tmp = it->cislot;
                                        while(tmp->linked_next != slot)
@@ -587,6 +605,8 @@ void eDVBCIInterfaces::removePMTHandler(eDVBServicePMTHandler *pmthandler)
                                        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;
                        }
@@ -871,14 +891,14 @@ RESULT eDVBCIInterfaces::setDescrambleRules(int slotid, SWIG_PYOBJECT(ePyObject)
        {
                --size;
                ePyObject caid = PyList_GET_ITEM(caid_list, size);
-               if (!PyInt_Check(caid))
+               if (!PyLong_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);
+               int tmpcaid = PyLong_AsLong(caid);
                if (tmpcaid > 0 && tmpcaid < 0x10000)
                        slot->possible_caids.insert(tmpcaid);
                else
@@ -887,6 +907,39 @@ RESULT eDVBCIInterfaces::setDescrambleRules(int slotid, SWIG_PYOBJECT(ePyObject)
        return 0;
 }
 
+PyObject *eDVBCIInterfaces::readCICaIds(int slotid)
+{
+       eDVBCISlot *slot = getSlot(slotid);
+       if (!slot)
+       {
+               char tmp[255];
+               snprintf(tmp, 255, "eDVBCIInterfaces::readCICaIds try to get CAIds for CI Slot %d... but just %d slots are available", slotid, m_slots.size());
+               PyErr_SetString(PyExc_StandardError, tmp);
+       }
+       else
+       {
+               int idx=0;
+               eDVBCICAManagerSession *ca_manager = slot->getCAManager();
+               const std::vector<uint16_t> *ci_caids = ca_manager ? &ca_manager->getCAIDs() : 0;
+               ePyObject list = PyList_New(ci_caids ? ci_caids->size() : 0);
+               if (ci_caids)
+               {
+                       for (std::vector<uint16_t>::const_iterator it = ci_caids->begin(); it != ci_caids->end(); ++it)
+                               PyList_SET_ITEM(list, idx++, PyLong_FromLong(*it));
+               }
+               return list;
+       }
+       return 0;
+}
+
+int eDVBCIInterfaces::setCIClockRate(int slotid, int rate)
+{
+       eDVBCISlot *slot = getSlot(slotid);
+       if (slot)
+               return slot->setClockRate(rate);
+       return -1;
+}
+
 int eDVBCISlot::send(const unsigned char *data, size_t len)
 {
        int res=0;
@@ -912,6 +965,7 @@ int eDVBCISlot::send(const unsigned char *data, size_t len)
 
 void eDVBCISlot::data(int what)
 {
+       eDebugCI("CISlot %d what %d\n", getSlotID(), what);
        if(what == eSocketNotifier::Priority) {
                if(state != stateRemoved) {
                        state = stateRemoved;
@@ -981,6 +1035,7 @@ eDVBCISlot::eDVBCISlot(eMainloop *context, int nr)
        use_count = 0;
        linked_next = 0;
        user_mapped = false;
+       plugged = true;
        
        slotid = nr;
 
@@ -992,7 +1047,7 @@ eDVBCISlot::eDVBCISlot(eMainloop *context, int nr)
 
        fd = ::open(filename, O_RDWR | O_NONBLOCK);
 
-       eDebug("CI Slot %d has fd %d", getSlotID(), fd);
+       eDebugCI("CI Slot %d has fd %d", getSlotID(), fd);
        state = stateInvalid;
 
        if (fd >= 0)
@@ -1007,6 +1062,7 @@ eDVBCISlot::eDVBCISlot(eMainloop *context, int nr)
 
 eDVBCISlot::~eDVBCISlot()
 {
+       eDVBCISession::deleteSessions(this);
 }
 
 void eDVBCISlot::setAppManager( eDVBCIApplicationManagerSession *session )
@@ -1132,10 +1188,11 @@ int eDVBCISlot::sendCAPMT(eDVBServicePMTHandler *pmthandler, const std::vector<u
                uint16_t program_number = ref.getServiceID().get();
                std::map<uint16_t, uint8_t>::iterator it =
                        running_services.find(program_number);
+               bool sendEmpty = caids.size() == 1 && caids[0] == 0xFFFF;
 
                if ( it != running_services.end() &&
                        (pmt_version == it->second) &&
-                       !(caids.size() == 1 && caids[0] == 0xFFFF) )
+                       !sendEmpty )
                {
                        eDebug("[eDVBCISlot] dont send self capmt version twice");
                        return -1;
@@ -1160,7 +1217,7 @@ int eDVBCISlot::sendCAPMT(eDVBServicePMTHandler *pmthandler, const std::vector<u
                                capmt.append(*i++);
                        }
                        capmt.writeToBuffer(raw_data);
-#if 1
+
 // begin calc capmt length
                        int wp=0;
                        int hlen;
@@ -1181,19 +1238,22 @@ int eDVBCISlot::sendCAPMT(eDVBServicePMTHandler *pmthandler, const std::vector<u
                                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)
+
+                       if (sendEmpty)
                        {
 //                             eDebugNoNewLine("SEND EMPTY CAPMT.. old version is %02x", raw_data[hlen+3]);
+                               if (sendEmpty && running_services.size() == 1)  // check if this is the capmt for the last running service
+                                       raw_data[hlen] = 0x03; // send only instead of update... because of strange effects with alphacrypt
                                raw_data[hlen+3] &= ~0x3E;
                                raw_data[hlen+3] |= ((pmt_version+1) & 0x1F) << 1;
 //                             eDebug(" new version is %02x", raw_data[hlen+3]);
                        }
 
+//                     eDebug("ca_manager %p dump capmt:", ca_manager);
+//                     for(int i=0;i<wp;i++)
+//                             eDebugNoNewLine("%02x ", raw_data[i]);
+//                     eDebug("");
+
                        //dont need tag and lenfield
                        ca_manager->sendCAPMT(raw_data + hlen, wp - hlen);
                        running_services[program_number] = pmt_version;
@@ -1269,4 +1329,21 @@ int eDVBCISlot::setSource(data_source source)
        return 0;
 }
 
+int eDVBCISlot::setClockRate(int rate)
+{
+       char buf[64];
+       snprintf(buf, 64, "/proc/stb/tsmux/ci%d_tsclk", slotid);
+       FILE *ci = fopen(buf, "wb");
+       if (ci)
+       {
+               if (rate)
+                       fprintf(ci, "high");
+               else
+                       fprintf(ci, "normal");
+               fclose(ci);
+               return 0;
+       }
+       return -1;
+}
+
 eAutoInitP0<eDVBCIInterfaces> init_eDVBCIInterfaces(eAutoInitNumbers::dvb, "CI Slots");