add ability to choose "Multiple service support (auto/no/yes)" in Ci Setup
[enigma2.git] / lib / dvb_ci / dvbci.cpp
1 #include <fcntl.h>
2 #include <sys/ioctl.h>
3
4 #include <lib/base/init.h>
5 #include <lib/base/init_num.h>
6 #include <lib/base/ebase.h>
7
8 #include <lib/base/eerror.h>
9 #include <lib/base/nconfig.h> // access to python config
10 #include <lib/dvb/pmt.h>
11 #include <lib/dvb_ci/dvbci.h>
12 #include <lib/dvb_ci/dvbci_session.h>
13 #include <lib/dvb_ci/dvbci_camgr.h>
14 #include <lib/dvb_ci/dvbci_ui.h>
15 #include <lib/dvb_ci/dvbci_appmgr.h>
16 #include <lib/dvb_ci/dvbci_mmi.h>
17
18 #include <dvbsi++/ca_program_map_section.h>
19
20 eDVBCIInterfaces *eDVBCIInterfaces::instance = 0;
21
22 eDVBCIInterfaces::eDVBCIInterfaces()
23 {
24         int num_ci = 0;
25         
26         instance = this;
27         
28         eDebug("scanning for common interfaces..");
29
30         while (1)
31         {
32                 struct stat s;
33                 char filename[128];
34                 sprintf(filename, "/dev/ci%d", num_ci);
35
36                 if (stat(filename, &s))
37                         break;
38
39                 ePtr<eDVBCISlot> cislot;
40
41                 cislot = new eDVBCISlot(eApp, num_ci);
42                 m_slots.push_back(cislot);
43
44                 ++num_ci;
45         }
46
47         eDebug("done, found %d common interface slots", num_ci);
48 }
49
50 eDVBCIInterfaces::~eDVBCIInterfaces()
51 {
52 }
53
54 eDVBCIInterfaces *eDVBCIInterfaces::getInstance()
55 {
56         return instance;
57 }
58
59 eDVBCISlot *eDVBCIInterfaces::getSlot(int slotid)
60 {
61         for(eSmartPtrList<eDVBCISlot>::iterator i(m_slots.begin()); i != m_slots.end(); ++i)
62                 if(i->getSlotID() == slotid)
63                         return i;
64
65         printf("FIXME: request for unknown slot\n");
66                         
67         return 0;
68 }
69
70 int eDVBCIInterfaces::getSlotState(int slotid)
71 {
72         eDVBCISlot *slot;
73
74         if( (slot = getSlot(slotid)) == 0 )
75                 return eDVBCISlot::stateInvalid;
76
77         return slot->getState();
78 }
79
80 int eDVBCIInterfaces::reset(int slotid)
81 {
82         eDVBCISlot *slot;
83
84         if( (slot = getSlot(slotid)) == 0 )
85                 return -1;
86
87         eDVBCISession::deleteSessions(slot);
88         ciRemoved(slot);
89
90         return slot->reset();
91 }
92
93 int eDVBCIInterfaces::enableTS(int slotid, int enable)
94 {
95         eDVBCISlot *slot;
96
97         if( (slot = getSlot(slotid)) == 0 )
98                 return -1;
99
100         int tunernum = 0;
101         PMTHandlerList::iterator it = m_pmt_handlers.begin();
102         while (it != m_pmt_handlers.end())
103         {
104                 if ( it->cislot == slot )
105                 {
106                         eDVBServicePMTHandler *pmthandler = it->pmthandler;
107                         eUsePtr<iDVBChannel> channel;
108                         if (!pmthandler->getChannel(channel))
109                         {
110                                 ePtr<iDVBFrontend> frontend;
111                                 if (!channel->getFrontend(frontend))
112                                 {
113                                         eDVBFrontend *fe = (eDVBFrontend*) &(*frontend);
114                                         tunernum = fe->getID();
115                                 }
116                         }
117                         break;
118                 }
119                 ++it;
120         }
121         return slot->enableTS(enable, tunernum);
122 }
123
124 int eDVBCIInterfaces::initialize(int slotid)
125 {
126         eDVBCISlot *slot;
127
128         if( (slot = getSlot(slotid)) == 0 )
129                 return -1;
130
131         slot->removeService();
132
133         return sendCAPMT(slotid);
134 }
135
136 int eDVBCIInterfaces::sendCAPMT(int slotid)
137 {
138         eDVBCISlot *slot;
139
140         if( (slot = getSlot(slotid)) == 0 )
141                 return -1;
142
143         PMTHandlerList::iterator it = m_pmt_handlers.begin();
144         while (it != m_pmt_handlers.end())
145         {
146                 if ( it->cislot == slot )
147                         slot->sendCAPMT(it->pmthandler);  // send capmt
148                 ++it;
149         }
150
151         return 0;
152 }
153
154 int eDVBCIInterfaces::startMMI(int slotid)
155 {
156         eDVBCISlot *slot;
157
158         if( (slot = getSlot(slotid)) == 0 )
159                 return -1;
160         
161         return slot->startMMI();
162 }
163
164 int eDVBCIInterfaces::stopMMI(int slotid)
165 {
166         eDVBCISlot *slot;
167
168         if( (slot = getSlot(slotid)) == 0 )
169                 return -1;
170         
171         return slot->stopMMI();
172 }
173
174 int eDVBCIInterfaces::answerText(int slotid, int answer)
175 {
176         eDVBCISlot *slot;
177
178         if( (slot = getSlot(slotid)) == 0 )
179                 return -1;
180         
181         return slot->answerText(answer);
182 }
183
184 int eDVBCIInterfaces::answerEnq(int slotid, char *value)
185 {
186         eDVBCISlot *slot;
187
188         if( (slot = getSlot(slotid)) == 0 )
189                 return -1;
190         
191         return slot->answerEnq(value);
192 }
193
194 int eDVBCIInterfaces::cancelEnq(int slotid)
195 {
196         eDVBCISlot *slot;
197
198         if( (slot = getSlot(slotid)) == 0 )
199                 return -1;
200         
201         return slot->cancelEnq();
202 }
203
204 void eDVBCIInterfaces::ciRemoved(eDVBCISlot *slot)
205 {
206         for (PMTHandlerList::iterator it(m_pmt_handlers.begin());
207                 it != m_pmt_handlers.end(); ++it)
208         {
209                 if (it->cislot == slot)
210                 {
211                         eServiceReferenceDVB ref;
212                         it->pmthandler->getServiceReference(ref);
213                         slot->removeService(ref.getServiceID().get());
214                         if (!--slot->use_count)
215                                 enableTS(slot->getSlotID(), 0);
216                         it->cislot=0;
217                 }
218         }
219 }
220
221 static bool canDescrambleMultipleServices(int slotid)
222 {
223         char configStr[255];
224         snprintf(configStr, 255, "config.ci%d.canDescrambleMultipleServices", slotid);
225         std::string str;
226         ePythonConfigQuery::getConfigValue(configStr, str);
227         eDebug("str is %s", str.empty()?"empty" : str.c_str());
228         if ( str == "auto" )
229         {
230                 std::string appname = eDVBCI_UI::getInstance()->getAppName(slotid);
231                 if (appname.find("AlphaCrypt") != std::string::npos)
232                         return true;
233         }
234         else if (str == "yes")
235                 return true;
236         return false;
237 }
238
239 void eDVBCIInterfaces::recheckPMTHandlers()
240 {
241 //      eDebug("recheckPMTHAndlers()");
242         for (PMTHandlerList::iterator it(m_pmt_handlers.begin());
243                 it != m_pmt_handlers.end(); ++it)
244         {
245                 CAID_LIST caids;
246                 ePtr<eDVBService> service;
247                 eServiceReferenceDVB ref;
248                 eDVBServicePMTHandler *pmthandler = it->pmthandler;
249                 eDVBServicePMTHandler::program p;
250
251                 pmthandler->getServiceReference(ref);
252                 pmthandler->getService(service);
253                 if (!pmthandler->getProgramInfo(p))
254                 {
255                         int cnt=0;
256                         for (std::set<uint16_t>::reverse_iterator x(p.caids.rbegin()); x != p.caids.rend(); ++x, ++cnt)
257                                 caids.push_front(*x);
258                         if (service && cnt)
259                                 service->m_ca = caids;
260                 }
261
262                 if (it->cislot)
263                         continue; // already running
264
265                 if (service)
266                         caids = service->m_ca;
267
268                 if (!caids.empty())
269                 {
270                         for (eSmartPtrList<eDVBCISlot>::iterator ci_it(m_slots.begin()); ci_it != m_slots.end(); ++ci_it)
271                         {
272                                 bool useThis=false;
273                                 eDVBCICAManagerSession *ca_manager = ci_it->getCAManager();
274                                 if (ca_manager)
275                                 {
276                                         const std::vector<uint16_t> &ci_caids = ca_manager->getCAIDs();
277                                         for (CAID_LIST::iterator ca(caids.begin()); ca != caids.end(); ++ca)
278                                         {
279                                                 std::vector<uint16_t>::const_iterator z =
280                                                         std::lower_bound(ci_caids.begin(), ci_caids.end(), *ca);
281                                                 if ( z != ci_caids.end() && *z == *ca )
282                                                 {
283                                                         eDebug("found ci for caid %04x", *z);
284                                                         useThis=true;
285                                                         break;
286                                                 }
287                                         }
288                                 }
289
290                                 if (useThis)
291                                 {
292                                         bool send_ca_pmt = false;
293                                         if (ci_it->use_count)  // check if this CI can descramble more than one service
294                                         {
295                                                 PMTHandlerList::iterator tmp = m_pmt_handlers.begin();
296                                                 while (tmp != m_pmt_handlers.end())
297                                                 {
298                                                         if ( tmp->cislot )
299                                                         {
300                                                                 eServiceReferenceDVB ref2;
301                                                                 tmp->pmthandler->getServiceReference(ref2);
302                                                                 eDVBChannelID s1, s2;
303                                                                 if (ref != ref2)
304                                                                 {
305                                                                         ref.getChannelID(s1);
306                                                                         ref2.getChannelID(s2);
307                                                                 }
308                                                                 if (ref == ref2 || (s1 == s2 && canDescrambleMultipleServices(ci_it->getSlotID())))
309                                                                 {
310                                                                         it->cislot = tmp->cislot;
311                                                                         ++it->cislot->use_count;
312                                                                         send_ca_pmt = true;
313 //                                                                      eDebug("usecount now %d", it->cislot->use_count);
314                                                                         break;
315                                                                 }
316                                                         }
317                                                         ++tmp;
318                                                 }
319                                         }
320                                         else
321                                         {
322                                                 ci_it->use_count=1;
323                                                 it->cislot = ci_it;
324 //                                              eDebug("usecount now %d", it->cislot->use_count);
325                                                 enableTS(ci_it->getSlotID(), 1);
326                                                 send_ca_pmt = true;
327                                         }
328                                         if (send_ca_pmt)
329                                                 gotPMT(pmthandler);
330                                 }
331                         }
332                 }
333         }
334 }
335
336 void eDVBCIInterfaces::addPMTHandler(eDVBServicePMTHandler *pmthandler)
337 {
338         // check if this pmthandler is already registered
339         PMTHandlerList::iterator it = m_pmt_handlers.begin();
340         while (it != m_pmt_handlers.end())
341         {
342                 if ( *it++ == pmthandler )
343                         return;
344         }
345
346         eServiceReferenceDVB ref;
347         pmthandler->getServiceReference(ref);
348         eDebug("[eDVBCIInterfaces] addPMTHandler %s", ref.toString().c_str());
349
350         m_pmt_handlers.push_back(CIPmtHandler(pmthandler));
351         recheckPMTHandlers();
352 }
353
354 void eDVBCIInterfaces::removePMTHandler(eDVBServicePMTHandler *pmthandler)
355 {
356         PMTHandlerList::iterator it=std::find(m_pmt_handlers.begin(),m_pmt_handlers.end(),pmthandler);
357         if (it != m_pmt_handlers.end())
358         {
359                 eDVBCISlot *slot = it->cislot;
360                 eDVBServicePMTHandler *pmthandler = it->pmthandler;
361                 m_pmt_handlers.erase(it);
362
363                 eServiceReferenceDVB service_to_remove;
364                 pmthandler->getServiceReference(service_to_remove);
365
366                 bool sameServiceExist=false;
367                 for (PMTHandlerList::iterator i=m_pmt_handlers.begin(); i != m_pmt_handlers.end(); ++i)
368                 {
369                         if (i->cislot)
370                         {
371                                 eServiceReferenceDVB ref;
372                                 i->pmthandler->getServiceReference(ref);
373                                 if ( ref == service_to_remove )
374                                 {
375                                         sameServiceExist=true;
376                                         break;
377                                 }
378                         }
379                 }
380
381                 if (slot && !sameServiceExist)
382                 {
383                         if (slot->getNumOfServices() > 1)
384                         {
385                                 eDebug("[eDVBCIInterfaces] remove last pmt handler for service %s send empty capmt",
386                                         service_to_remove.toString().c_str());
387                                 std::vector<uint16_t> caids;
388                                 caids.push_back(0xFFFF);
389                                 slot->sendCAPMT(pmthandler, caids);  // send a capmt without caids to remove a running service
390                         }
391                         slot->removeService(service_to_remove.getServiceID().get());
392                 }
393
394                 if (slot && !--slot->use_count)
395                 {
396                         ASSERT(!slot->getNumOfServices());
397                         enableTS(slot->getSlotID(),0);
398                 }
399         }
400         // check if another service is waiting for the CI
401         recheckPMTHandlers();
402 }
403
404 void eDVBCIInterfaces::gotPMT(eDVBServicePMTHandler *pmthandler)
405 {
406         eDebug("[eDVBCIInterfaces] gotPMT");
407         PMTHandlerList::iterator it=std::find(m_pmt_handlers.begin(), m_pmt_handlers.end(), pmthandler);
408         if (it != m_pmt_handlers.end() && it->cislot)
409                 it->cislot->sendCAPMT(pmthandler);
410 }
411
412 int eDVBCIInterfaces::getMMIState(int slotid)
413 {
414         eDVBCISlot *slot;
415
416         if( (slot = getSlot(slotid)) == 0 )
417                 return -1;
418         
419         return slot->getMMIState();
420 }
421
422 int eDVBCISlot::send(const unsigned char *data, size_t len)
423 {
424         int res=0;
425         //int i;
426         //printf("< ");
427         //for(i=0;i<len;i++)
428         //      printf("%02x ",data[i]);
429         //printf("\n");
430
431         if (sendqueue.empty())
432                 res = ::write(fd, data, len);
433
434         if (res < 0 || (unsigned int)res != len)
435         {
436                 unsigned char *d = new unsigned char[len];
437                 memcpy(d, data, len);
438                 sendqueue.push( queueData(d, len) );
439                 notifier->setRequested(eSocketNotifier::Read | eSocketNotifier::Priority | eSocketNotifier::Write);
440         }
441
442         return res;
443 }
444
445 void eDVBCISlot::data(int what)
446 {
447         if(what == eSocketNotifier::Priority) {
448                 if(state != stateRemoved) {
449                         state = stateRemoved;
450                         printf("ci removed\n");
451                         while(sendqueue.size())
452                         {
453                                 delete [] sendqueue.top().data;
454                                 sendqueue.pop();
455                         }
456                         eDVBCIInterfaces::getInstance()->ciRemoved(this);
457                         eDVBCISession::deleteSessions(this);
458                         notifier->setRequested(eSocketNotifier::Read);
459                         eDVBCI_UI::getInstance()->setState(getSlotID(),0);
460                 }
461                 return;
462         }
463
464         if (state == stateInvalid)
465                 reset();
466
467         if(state != stateInserted) {
468                 eDebug("ci inserted");
469                 state = stateInserted;
470                 eDVBCI_UI::getInstance()->setState(getSlotID(),1);
471                 notifier->setRequested(eSocketNotifier::Read|eSocketNotifier::Priority);
472                 /* enable PRI to detect removal or errors */
473         }
474
475         if (what & eSocketNotifier::Read) {
476                 __u8 data[4096];
477                 int r;
478                 r = ::read(fd, data, 4096);
479                 if(r > 0) {
480 //                      int i;
481 //                      printf("> ");
482 //                      for(i=0;i<r;i++)
483 //                              printf("%02x ",data[i]);
484 //                      printf("\n");
485                         eDVBCISession::receiveData(this, data, r);
486                         eDVBCISession::pollAll();
487                         return;
488                 }
489         }
490         else if (what & eSocketNotifier::Write) {
491                 if (!sendqueue.empty()) {
492                         const queueData &qe = sendqueue.top();
493                         int res = ::write(fd, qe.data, qe.len);
494                         if (res >= 0 && (unsigned int)res == qe.len)
495                         {
496                                 delete [] qe.data;
497                                 sendqueue.pop();
498                         }
499                 }
500                 else
501                         notifier->setRequested(eSocketNotifier::Read|eSocketNotifier::Priority);
502         }
503 }
504
505 DEFINE_REF(eDVBCISlot);
506
507 eDVBCISlot::eDVBCISlot(eMainloop *context, int nr)
508 {
509         char filename[128];
510
511         application_manager = 0;
512         mmi_session = 0;
513         ca_manager = 0;
514         use_count = 0;
515         
516         slotid = nr;
517
518         sprintf(filename, "/dev/ci%d", nr);
519
520         fd = ::open(filename, O_RDWR | O_NONBLOCK);
521
522         eDebug("eDVBCISlot has fd %d", fd);
523         state = stateInvalid;
524
525         if (fd >= 0)
526         {
527                 notifier = new eSocketNotifier(context, fd, eSocketNotifier::Read | eSocketNotifier::Priority | eSocketNotifier::Write);
528                 CONNECT(notifier->activated, eDVBCISlot::data);
529         } else
530         {
531                 perror(filename);
532         }
533
534         enableTS(0, 0);
535 }
536
537 eDVBCISlot::~eDVBCISlot()
538 {
539 }
540
541 void eDVBCISlot::setAppManager( eDVBCIApplicationManagerSession *session )
542 {
543         application_manager=session;
544 }
545
546 void eDVBCISlot::setMMIManager( eDVBCIMMISession *session )
547 {
548         mmi_session = session;
549 }
550
551 void eDVBCISlot::setCAManager( eDVBCICAManagerSession *session )
552 {
553         ca_manager = session;
554 }
555
556 int eDVBCISlot::getSlotID()
557 {
558         return slotid;
559 }
560
561 int eDVBCISlot::reset()
562 {
563         printf("edvbcislot: reset requested\n");
564
565         if (state == stateInvalid)
566         {
567                 unsigned char buf[256];
568                 eDebug("ci flush");
569                 while(::read(fd, buf, 256)>0);
570                 state = stateResetted;
571         }
572
573         while(sendqueue.size())
574         {
575                 delete [] sendqueue.top().data;
576                 sendqueue.pop();
577         }
578
579         ioctl(fd, 0);
580
581         return 0;
582 }
583
584 int eDVBCISlot::startMMI()
585 {
586         printf("edvbcislot: startMMI()\n");
587         
588         if(application_manager)
589                 application_manager->startMMI();
590         
591         return 0;
592 }
593
594 int eDVBCISlot::stopMMI()
595 {
596         printf("edvbcislot: stopMMI()\n");
597
598         if(mmi_session)
599                 mmi_session->stopMMI();
600         
601         return 0;
602 }
603
604 int eDVBCISlot::answerText(int answer)
605 {
606         printf("edvbcislot: answerText(%d)\n", answer);
607
608         if(mmi_session)
609                 mmi_session->answerText(answer);
610
611         return 0;
612 }
613
614 int eDVBCISlot::getMMIState()
615 {
616         if(mmi_session)
617                 return 1;
618
619         return 0;
620 }
621
622 int eDVBCISlot::answerEnq(char *value)
623 {
624         printf("edvbcislot: answerENQ(%s)\n", value);
625
626         if(mmi_session)
627                 mmi_session->answerEnq(value);
628
629         return 0;
630 }
631
632 int eDVBCISlot::cancelEnq()
633 {
634         printf("edvbcislot: cancelENQ\n");
635
636         if(mmi_session)
637                 mmi_session->cancelEnq();
638
639         return 0;
640 }
641
642 int eDVBCISlot::sendCAPMT(eDVBServicePMTHandler *pmthandler, const std::vector<uint16_t> &ids)
643 {
644         if (!ca_manager)
645         {
646                 eDebug("no ca_manager (no CI plugged?)");
647                 return -1;
648         }
649         const std::vector<uint16_t> &caids = ids.empty() ? ca_manager->getCAIDs() : ids;
650         ePtr<eTable<ProgramMapSection> > ptr;
651         if (pmthandler->getPMT(ptr))
652                 return -1;
653         else
654         {
655                 eDVBTableSpec table_spec;
656                 ptr->getSpec(table_spec);
657                 int pmt_version = table_spec.version & 0x1F; // just 5 bits
658
659                 eServiceReferenceDVB ref;
660                 pmthandler->getServiceReference(ref);
661                 uint16_t program_number = ref.getServiceID().get();
662                 std::map<uint16_t, uint8_t>::iterator it =
663                         running_services.find(program_number);
664
665                 if ( it != running_services.end() &&
666                         (pmt_version == it->second) &&
667                         !(caids.size() == 1 && caids[0] == 0xFFFF) )
668                 {
669                         eDebug("[eDVBCISlot] dont sent self capmt version twice");
670                         return -1;
671                 }
672
673                 std::vector<ProgramMapSection*>::const_iterator i=ptr->getSections().begin();
674                 if ( i == ptr->getSections().end() )
675                         return -1;
676                 else
677                 {
678                         unsigned char raw_data[2048];
679
680 //                      eDebug("send %s capmt for service %04x",
681 //                              it != running_services.end() ? "UPDATE" : running_services.empty() ? "ONLY" : "ADD",
682 //                              program_number);
683
684                         CaProgramMapSection capmt(*i++,
685                                 it != running_services.end() ? 0x05 /*update*/ : running_services.empty() ? 0x03 /*only*/ : 0x04 /*add*/, 0x01, caids );
686                         while( i != ptr->getSections().end() )
687                         {
688                 //                      eDebug("append");
689                                 capmt.append(*i++);
690                         }
691                         capmt.writeToBuffer(raw_data);
692 #if 1
693 // begin calc capmt length
694                         int wp=0;
695                         int hlen;
696                         if ( raw_data[3] & 0x80 )
697                         {
698                                 int i=0;
699                                 int lenbytes = raw_data[3] & ~0x80;
700                                 while(i < lenbytes)
701                                         wp = (wp << 8) | raw_data[4 + i++];
702                                 wp+=4;
703                                 wp+=lenbytes;
704                                 hlen = 4 + lenbytes;
705                         }
706                         else
707                         {
708                                 wp = raw_data[3];
709                                 wp+=4;
710                                 hlen = 4;
711                         }
712 // end calc capmt length
713 //                      eDebug("ca_manager %p dump capmt:", ca_manager);
714 //                      for(int i=0;i<wp;i++)
715 //                              eDebugNoNewLine("%02x ", raw_data[i]);
716 //                      eDebug("");
717 #endif
718                         if (caids.size() == 1 && caids[0] == 0xFFFF)
719                         {
720 //                              eDebugNoNewLine("SEND EMPTY CAPMT.. old version is %02x", raw_data[hlen+3]);
721                                 raw_data[hlen+3] &= ~0x3E;
722                                 raw_data[hlen+3] |= ((pmt_version+1) & 0x1F) << 1;
723 //                              eDebug(" new version is %02x", raw_data[hlen+3]);
724                         }
725
726                         //dont need tag and lenfield
727                         ca_manager->sendCAPMT(raw_data + hlen, wp - hlen);
728                         running_services[program_number] = pmt_version;
729                 }
730         }
731         return 0;
732 }
733
734 void eDVBCISlot::removeService(uint16_t program_number)
735 {
736         if (program_number == 0xFFFF)
737                 running_services.clear();  // remove all
738         else
739                 running_services.erase(program_number);  // remove single service
740 }
741
742 int eDVBCISlot::enableTS(int enable, int tuner)
743 {
744 //      printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
745 //      printf("eDVBCISlot::enableTS(%d %d)\n", enable, tuner);
746
747         FILE *input0, *input1, *ci;
748         if((input0 = fopen("/proc/stb/tsmux/input0", "wb")) == NULL) {
749                 printf("cannot open /proc/stb/tsmux/input0\n");
750                 return 0;
751         }
752         if((input1 = fopen("/proc/stb/tsmux/input1", "wb")) == NULL) {
753                 printf("cannot open /proc/stb/tsmux/input1\n");
754                 return 0;
755         }
756         if((ci = fopen("/proc/stb/tsmux/input2", "wb")) == NULL) {
757                 printf("cannot open /proc/stb/tsmux/input2\n");
758                 return 0;
759         }
760
761         fprintf(ci, "%s", tuner==0 ? "A" : "B");  // configure CI data source (TunerA, TunerB)
762         fprintf(input0, "%s", tuner==0 && enable ? "CI" : "A"); // configure ATI input 0 data source
763         fprintf(input1, "%s", tuner==1 && enable ? "CI" : "B"); // configure ATI input 1 data source
764
765         fclose(input0);
766         fclose(input1);
767         fclose(ci);
768         return 0;
769 }
770
771 eAutoInitP0<eDVBCIInterfaces> init_eDVBCIInterfaces(eAutoInitNumbers::dvb, "CI Slots");