warn instead of fatal if bitmap is incompatible.
[enigma2.git] / lib / dvb_ci / dvbci.cpp
index c24ddd2569367cfcaa037ffad59733722905f251..1f1fa1663481265f1cf5b874e9f978e187197078 100644 (file)
@@ -6,6 +6,7 @@
 #include <lib/base/ebase.h>
 
 #include <lib/base/eerror.h>
+#include <lib/base/nconfig.h> // access to python config
 #include <lib/dvb/pmt.h>
 #include <lib/dvb_ci/dvbci.h>
 #include <lib/dvb_ci/dvbci_session.h>
@@ -61,7 +62,7 @@ eDVBCISlot *eDVBCIInterfaces::getSlot(int slotid)
                if(i->getSlotID() == slotid)
                        return i;
 
-       printf("FIXME: request for unknown slot\n");
+       eDebug("FIXME: request for unknown slot");
                        
        return 0;
 }
@@ -89,37 +90,6 @@ int eDVBCIInterfaces::reset(int slotid)
        return slot->reset();
 }
 
-int eDVBCIInterfaces::enableTS(int slotid, int enable)
-{
-       eDVBCISlot *slot;
-
-       if( (slot = getSlot(slotid)) == 0 )
-               return -1;
-
-       int tunernum = 0;
-       PMTHandlerList::iterator it = m_pmt_handlers.begin();
-       while (it != m_pmt_handlers.end())
-       {
-               if ( it->cislot == slot )
-               {
-                       eDVBServicePMTHandler *pmthandler = it->pmthandler;
-                       eUsePtr<iDVBChannel> channel;
-                       if (!pmthandler->getChannel(channel))
-                       {
-                               ePtr<iDVBFrontend> frontend;
-                               if (!channel->getFrontend(frontend))
-                               {
-                                       eDVBFrontend *fe = (eDVBFrontend*) &(*frontend);
-                                       tunernum = fe->getID();
-                               }
-                       }
-                       break;
-               }
-               ++it;
-       }
-       return slot->enableTS(enable, tunernum);
-}
-
 int eDVBCIInterfaces::initialize(int slotid)
 {
        eDVBCISlot *slot;
@@ -142,8 +112,14 @@ int eDVBCIInterfaces::sendCAPMT(int slotid)
        PMTHandlerList::iterator it = m_pmt_handlers.begin();
        while (it != m_pmt_handlers.end())
        {
-               if ( it->cislot == slot )
-                       slot->sendCAPMT(it->pmthandler);  // send capmt
+               eDVBCISlot *tmp = it->cislot;
+               while (tmp != slot)
+                       tmp = tmp->linked_next;
+               if (tmp)
+               {
+                       tmp->sendCAPMT(it->pmthandler);  // send capmt
+                       break;
+               }
                ++it;
        }
 
@@ -205,18 +181,51 @@ void eDVBCIInterfaces::ciRemoved(eDVBCISlot *slot)
        for (PMTHandlerList::iterator it(m_pmt_handlers.begin());
                it != m_pmt_handlers.end(); ++it)
        {
-               if (it->cislot == slot)
+               eServiceReferenceDVB ref;
+               it->pmthandler->getServiceReference(ref);
+               slot->removeService(ref.getServiceID().get());
+               if (slot->use_count && !--slot->use_count)
                {
-                       eServiceReferenceDVB ref;
-                       it->pmthandler->getServiceReference(ref);
-                       slot->removeService(ref.getServiceID().get());
-                       if (!--slot->use_count)
-                               enableTS(slot->getSlotID(), 0);
-                       it->cislot=0;
+                       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()");
@@ -240,9 +249,6 @@ void eDVBCIInterfaces::recheckPMTHandlers()
                                service->m_ca = caids;
                }
 
-               if (it->cislot)
-                       continue; // already running
-
                if (service)
                        caids = service->m_ca;
 
@@ -250,9 +256,6 @@ void eDVBCIInterfaces::recheckPMTHandlers()
                {
                        for (eSmartPtrList<eDVBCISlot>::iterator ci_it(m_slots.begin()); ci_it != m_slots.end(); ++ci_it)
                        {
-                               if (ci_it->getState() == eDVBCISlot::stateInvalid)
-                                       ci_it->reset();
-
                                bool useThis=false;
                                eDVBCICAManagerSession *ca_manager = ci_it->getCAManager();
                                if (ca_manager)
@@ -264,7 +267,7 @@ void eDVBCIInterfaces::recheckPMTHandlers()
                                                        std::lower_bound(ci_caids.begin(), ci_caids.end(), *ca);
                                                if ( z != ci_caids.end() && *z == *ca )
                                                {
-                                                       eDebug("found ci for caid %04x", *z);
+//                                                     eDebug("found ci for caid %04x", *z);
                                                        useThis=true;
                                                        break;
                                                }
@@ -273,15 +276,14 @@ void eDVBCIInterfaces::recheckPMTHandlers()
 
                                if (useThis)
                                {
-                                       bool send_ca_pmt = false;
                                        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 )
+                                                       if ( tmp->cislot == ci_it && it != tmp )
                                                        {
-                                                               bool canHandleMultipleServices=false;
                                                                eServiceReferenceDVB ref2;
                                                                tmp->pmthandler->getServiceReference(ref2);
                                                                eDVBChannelID s1, s2;
@@ -289,36 +291,83 @@ void eDVBCIInterfaces::recheckPMTHandlers()
                                                                {
                                                                        ref.getChannelID(s1);
                                                                        ref2.getChannelID(s2);
-                                                                       // FIXME .. build a "ci can handle multiple services" config entry
-                                                                       // Yes / No / Auto
-                                                                       if ( eDVBCI_UI::getInstance()->getAppName(ci_it->getSlotID()) == "AlphaCrypt" )
-                                                                       {
-                                                                               canHandleMultipleServices = true;
-                                                                               eDebug("Alphacrypt can handle multiple services");
-                                                                       }
                                                                }
-                                                               if (ref == ref2 || (s1 == s2 && canHandleMultipleServices) )
+                                                               if (ref == ref2 || (s1 == s2 && canDescrambleMultipleServices(ci_it->getSlotID())))
                                                                {
-                                                                       it->cislot = tmp->cislot;
-                                                                       ++it->cislot->use_count;
-                                                                       send_ca_pmt = true;
-//                                                                     eDebug("usecount now %d", it->cislot->use_count);
+                                                                       useThis = true;
                                                                        break;
                                                                }
                                                        }
                                                        ++tmp;
                                                }
                                        }
-                                       else
+                                       if (useThis)
                                        {
-                                               ci_it->use_count=1;
+                                               // check if this CI is already assigned to this pmthandler
+                                               eDVBCISlot *tmp = it->cislot;
+                                               while(tmp)
+                                               {
+                                                       if (tmp == ci_it)
+                                                               break;
+                                               }
+
+                                               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->getID();
+                                                               }
+                                                       }
+                                                       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;
-//                                             eDebug("usecount now %d", it->cislot->use_count);
-                                               enableTS(ci_it->getSlotID(), 1);
-                                               send_ca_pmt = true;
-                                       }
-                                       if (send_ca_pmt)
                                                gotPMT(pmthandler);
+                                       }
                                }
                        }
                }
@@ -358,32 +407,58 @@ void eDVBCIInterfaces::removePMTHandler(eDVBServicePMTHandler *pmthandler)
                bool sameServiceExist=false;
                for (PMTHandlerList::iterator i=m_pmt_handlers.begin(); i != m_pmt_handlers.end(); ++i)
                {
-                       eServiceReferenceDVB ref;
-                       i->pmthandler->getServiceReference(ref);
-                       if ( ref == service_to_remove )
+                       if (i->cislot)
                        {
-                               sameServiceExist=true;
-                               break;
+                               eServiceReferenceDVB ref;
+                               i->pmthandler->getServiceReference(ref);
+                               if ( ref == service_to_remove )
+                               {
+                                       sameServiceExist=true;
+                                       break;
+                               }
                        }
                }
 
-               if (slot && !sameServiceExist)
+               while(slot)
                {
-                       if (slot->getNumOfServices() > 1)
+                       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
+                               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());
                        }
-                       slot->removeService(service_to_remove.getServiceID().get());
-               }
 
-               if (slot && !--slot->use_count)
-               {
-                       ASSERT(!slot->getNumOfServices());
-                       enableTS(slot->getSlotID(),0);
+                       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
@@ -395,7 +470,14 @@ 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)
-               it->cislot->sendCAPMT(pmthandler);
+       {
+               eDVBCISlot *tmp = it->cislot;
+               while(tmp)
+               {
+                       tmp->sendCAPMT(pmthandler);
+                       tmp = tmp->linked_next;
+               }
+       }
 }
 
 int eDVBCIInterfaces::getMMIState(int slotid)
@@ -408,14 +490,99 @@ int eDVBCIInterfaces::getMMIState(int slotid)
        return slot->getMMIState();
 }
 
+int eDVBCIInterfaces::setInputSource(int tuner_no, data_source source)
+{
+//     eDebug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+//     eDebug("eDVBCIInterfaces::setInputSource(%d %d)", tuner_no, (int)source);
+#if defined(DM8000)
+       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 (input > 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 // force 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);
+#endif
+       eDebug("eDVBCIInterfaces->setInputSource(%d, %d)", tuner_no, (int)source);
+       return 0;
+}
+
+
 int eDVBCISlot::send(const unsigned char *data, size_t len)
 {
        int res=0;
        //int i;
-       //printf("< ");
+       //eDebugNoNewLine("< ");
        //for(i=0;i<len;i++)
-       //      printf("%02x ",data[i]);
-       //printf("\n");
+       //      eDebugNoNewLine("%02x ",data[i]);
+       //eDebug("");
 
        if (sendqueue.empty())
                res = ::write(fd, data, len);
@@ -436,7 +603,7 @@ 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;
@@ -467,10 +634,10 @@ void eDVBCISlot::data(int what)
                r = ::read(fd, data, 4096);
                if(r > 0) {
 //                     int i;
-//                     printf("> ");
+//                     eDebugNoNewLine("> ");
 //                     for(i=0;i<r;i++)
-//                             printf("%02x ",data[i]);
-//                     printf("\n");
+//                             eDebugNoNewLine("%02x ",data[i]);
+//                     eDebug("");
                        eDVBCISession::receiveData(this, data, r);
                        eDVBCISession::pollAll();
                        return;
@@ -501,6 +668,7 @@ eDVBCISlot::eDVBCISlot(eMainloop *context, int nr)
        mmi_session = 0;
        ca_manager = 0;
        use_count = 0;
+       linked_next = 0;
        
        slotid = nr;
 
@@ -519,8 +687,6 @@ eDVBCISlot::eDVBCISlot(eMainloop *context, int nr)
        {
                perror(filename);
        }
-
-       enableTS(0, 0);
 }
 
 eDVBCISlot::~eDVBCISlot()
@@ -549,7 +715,7 @@ int eDVBCISlot::getSlotID()
 
 int eDVBCISlot::reset()
 {
-       printf("edvbcislot: reset requested\n");
+       eDebug("edvbcislot: reset requested");
 
        if (state == stateInvalid)
        {
@@ -572,7 +738,7 @@ int eDVBCISlot::reset()
 
 int eDVBCISlot::startMMI()
 {
-       printf("edvbcislot: startMMI()\n");
+       eDebug("edvbcislot: startMMI()");
        
        if(application_manager)
                application_manager->startMMI();
@@ -582,7 +748,7 @@ int eDVBCISlot::startMMI()
 
 int eDVBCISlot::stopMMI()
 {
-       printf("edvbcislot: stopMMI()\n");
+       eDebug("edvbcislot: stopMMI()");
 
        if(mmi_session)
                mmi_session->stopMMI();
@@ -592,7 +758,7 @@ int eDVBCISlot::stopMMI()
 
 int eDVBCISlot::answerText(int answer)
 {
-       printf("edvbcislot: answerText(%d)\n", answer);
+       eDebug("edvbcislot: answerText(%d)", answer);
 
        if(mmi_session)
                mmi_session->answerText(answer);
@@ -610,7 +776,7 @@ int eDVBCISlot::getMMIState()
 
 int eDVBCISlot::answerEnq(char *value)
 {
-       printf("edvbcislot: answerENQ(%s)\n", value);
+       eDebug("edvbcislot: answerENQ(%s)", value);
 
        if(mmi_session)
                mmi_session->answerEnq(value);
@@ -620,7 +786,7 @@ int eDVBCISlot::answerEnq(char *value)
 
 int eDVBCISlot::cancelEnq()
 {
-       printf("edvbcislot: cancelENQ\n");
+       eDebug("edvbcislot: cancelENQ");
 
        if(mmi_session)
                mmi_session->cancelEnq();
@@ -655,7 +821,7 @@ int eDVBCISlot::sendCAPMT(eDVBServicePMTHandler *pmthandler, const std::vector<u
                        (pmt_version == it->second) &&
                        !(caids.size() == 1 && caids[0] == 0xFFFF) )
                {
-                       eDebug("[eDVBCISlot] dont sent self capmt version twice");
+                       eDebug("[eDVBCISlot] dont send self capmt version twice");
                        return -1;
                }
 
@@ -728,32 +894,59 @@ void eDVBCISlot::removeService(uint16_t program_number)
                running_services.erase(program_number);  // remove single service
 }
 
-int eDVBCISlot::enableTS(int enable, int tuner)
+int eDVBCISlot::setSource(data_source source)
 {
-//     printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
-//     printf("eDVBCISlot::enableTS(%d %d)\n", enable, tuner);
-
-       FILE *input0, *input1, *ci;
-       if((input0 = fopen("/proc/stb/tsmux/input0", "wb")) == NULL) {
-               printf("cannot open /proc/stb/tsmux/input0\n");
-               return 0;
-       }
-       if((input1 = fopen("/proc/stb/tsmux/input1", "wb")) == NULL) {
-               printf("cannot open /proc/stb/tsmux/input1\n");
-               return 0;
+       current_source = source;
+#if defined(DM8000)
+       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;
        }
-       if((ci = fopen("/proc/stb/tsmux/input2", "wb")) == NULL) {
-               printf("cannot open /proc/stb/tsmux/input2\n");
+       fclose(ci);
+#else // force 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;
        }
-
-       fprintf(ci, "%s", tuner==0 ? "A" : "B");  // configure CI data source (TunerA, TunerB)
-       fprintf(input0, "%s", tuner==0 && enable ? "CI" : "A"); // configure ATI input 0 data source
-       fprintf(input1, "%s", tuner==1 && enable ? "CI" : "B"); // configure ATI input 1 data source
-
-       fclose(input0);
-       fclose(input1);
+       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);
+#endif
+       eDebug("eDVBCISlot->setSource(%d)", (int)source);
        return 0;
 }