remove debug
[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         if ( str == "auto" )
228         {
229                 std::string appname = eDVBCI_UI::getInstance()->getAppName(slotid);
230                 if (appname.find("AlphaCrypt") != std::string::npos)
231                         return true;
232         }
233         else if (str == "yes")
234                 return true;
235         return false;
236 }
237
238 void eDVBCIInterfaces::recheckPMTHandlers()
239 {
240 //      eDebug("recheckPMTHAndlers()");
241         for (PMTHandlerList::iterator it(m_pmt_handlers.begin());
242                 it != m_pmt_handlers.end(); ++it)
243         {
244                 CAID_LIST caids;
245                 ePtr<eDVBService> service;
246                 eServiceReferenceDVB ref;
247                 eDVBServicePMTHandler *pmthandler = it->pmthandler;
248                 eDVBServicePMTHandler::program p;
249
250                 pmthandler->getServiceReference(ref);
251                 pmthandler->getService(service);
252                 if (!pmthandler->getProgramInfo(p))
253                 {
254                         int cnt=0;
255                         for (std::set<uint16_t>::reverse_iterator x(p.caids.rbegin()); x != p.caids.rend(); ++x, ++cnt)
256                                 caids.push_front(*x);
257                         if (service && cnt)
258                                 service->m_ca = caids;
259                 }
260
261                 if (it->cislot)
262                         continue; // already running
263
264                 if (service)
265                         caids = service->m_ca;
266
267                 if (!caids.empty())
268                 {
269                         for (eSmartPtrList<eDVBCISlot>::iterator ci_it(m_slots.begin()); ci_it != m_slots.end(); ++ci_it)
270                         {
271                                 bool useThis=false;
272                                 eDVBCICAManagerSession *ca_manager = ci_it->getCAManager();
273                                 if (ca_manager)
274                                 {
275                                         const std::vector<uint16_t> &ci_caids = ca_manager->getCAIDs();
276                                         for (CAID_LIST::iterator ca(caids.begin()); ca != caids.end(); ++ca)
277                                         {
278                                                 std::vector<uint16_t>::const_iterator z =
279                                                         std::lower_bound(ci_caids.begin(), ci_caids.end(), *ca);
280                                                 if ( z != ci_caids.end() && *z == *ca )
281                                                 {
282                                                         eDebug("found ci for caid %04x", *z);
283                                                         useThis=true;
284                                                         break;
285                                                 }
286                                         }
287                                 }
288
289                                 if (useThis)
290                                 {
291                                         bool send_ca_pmt = false;
292                                         if (ci_it->use_count)  // check if this CI can descramble more than one service
293                                         {
294                                                 PMTHandlerList::iterator tmp = m_pmt_handlers.begin();
295                                                 while (tmp != m_pmt_handlers.end())
296                                                 {
297                                                         if ( tmp->cislot )
298                                                         {
299                                                                 eServiceReferenceDVB ref2;
300                                                                 tmp->pmthandler->getServiceReference(ref2);
301                                                                 eDVBChannelID s1, s2;
302                                                                 if (ref != ref2)
303                                                                 {
304                                                                         ref.getChannelID(s1);
305                                                                         ref2.getChannelID(s2);
306                                                                 }
307                                                                 if (ref == ref2 || (s1 == s2 && canDescrambleMultipleServices(ci_it->getSlotID())))
308                                                                 {
309                                                                         it->cislot = tmp->cislot;
310                                                                         ++it->cislot->use_count;
311                                                                         send_ca_pmt = true;
312 //                                                                      eDebug("usecount now %d", it->cislot->use_count);
313                                                                         break;
314                                                                 }
315                                                         }
316                                                         ++tmp;
317                                                 }
318                                         }
319                                         else
320                                         {
321                                                 ci_it->use_count=1;
322                                                 it->cislot = ci_it;
323 //                                              eDebug("usecount now %d", it->cislot->use_count);
324                                                 enableTS(ci_it->getSlotID(), 1);
325                                                 send_ca_pmt = true;
326                                         }
327                                         if (send_ca_pmt)
328                                                 gotPMT(pmthandler);
329                                 }
330                         }
331                 }
332         }
333 }
334
335 void eDVBCIInterfaces::addPMTHandler(eDVBServicePMTHandler *pmthandler)
336 {
337         // check if this pmthandler is already registered
338         PMTHandlerList::iterator it = m_pmt_handlers.begin();
339         while (it != m_pmt_handlers.end())
340         {
341                 if ( *it++ == pmthandler )
342                         return;
343         }
344
345         eServiceReferenceDVB ref;
346         pmthandler->getServiceReference(ref);
347         eDebug("[eDVBCIInterfaces] addPMTHandler %s", ref.toString().c_str());
348
349         m_pmt_handlers.push_back(CIPmtHandler(pmthandler));
350         recheckPMTHandlers();
351 }
352
353 void eDVBCIInterfaces::removePMTHandler(eDVBServicePMTHandler *pmthandler)
354 {
355         PMTHandlerList::iterator it=std::find(m_pmt_handlers.begin(),m_pmt_handlers.end(),pmthandler);
356         if (it != m_pmt_handlers.end())
357         {
358                 eDVBCISlot *slot = it->cislot;
359                 eDVBServicePMTHandler *pmthandler = it->pmthandler;
360                 m_pmt_handlers.erase(it);
361
362                 eServiceReferenceDVB service_to_remove;
363                 pmthandler->getServiceReference(service_to_remove);
364
365                 bool sameServiceExist=false;
366                 for (PMTHandlerList::iterator i=m_pmt_handlers.begin(); i != m_pmt_handlers.end(); ++i)
367                 {
368                         if (i->cislot)
369                         {
370                                 eServiceReferenceDVB ref;
371                                 i->pmthandler->getServiceReference(ref);
372                                 if ( ref == service_to_remove )
373                                 {
374                                         sameServiceExist=true;
375                                         break;
376                                 }
377                         }
378                 }
379
380                 if (slot && !sameServiceExist)
381                 {
382                         if (slot->getNumOfServices() > 1)
383                         {
384                                 eDebug("[eDVBCIInterfaces] remove last pmt handler for service %s send empty capmt",
385                                         service_to_remove.toString().c_str());
386                                 std::vector<uint16_t> caids;
387                                 caids.push_back(0xFFFF);
388                                 slot->sendCAPMT(pmthandler, caids);  // send a capmt without caids to remove a running service
389                         }
390                         slot->removeService(service_to_remove.getServiceID().get());
391                 }
392
393                 if (slot && !--slot->use_count)
394                 {
395                         ASSERT(!slot->getNumOfServices());
396                         enableTS(slot->getSlotID(),0);
397                 }
398         }
399         // check if another service is waiting for the CI
400         recheckPMTHandlers();
401 }
402
403 void eDVBCIInterfaces::gotPMT(eDVBServicePMTHandler *pmthandler)
404 {
405         eDebug("[eDVBCIInterfaces] gotPMT");
406         PMTHandlerList::iterator it=std::find(m_pmt_handlers.begin(), m_pmt_handlers.end(), pmthandler);
407         if (it != m_pmt_handlers.end() && it->cislot)
408                 it->cislot->sendCAPMT(pmthandler);
409 }
410
411 int eDVBCIInterfaces::getMMIState(int slotid)
412 {
413         eDVBCISlot *slot;
414
415         if( (slot = getSlot(slotid)) == 0 )
416                 return -1;
417         
418         return slot->getMMIState();
419 }
420
421 int eDVBCISlot::send(const unsigned char *data, size_t len)
422 {
423         int res=0;
424         //int i;
425         //printf("< ");
426         //for(i=0;i<len;i++)
427         //      printf("%02x ",data[i]);
428         //printf("\n");
429
430         if (sendqueue.empty())
431                 res = ::write(fd, data, len);
432
433         if (res < 0 || (unsigned int)res != len)
434         {
435                 unsigned char *d = new unsigned char[len];
436                 memcpy(d, data, len);
437                 sendqueue.push( queueData(d, len) );
438                 notifier->setRequested(eSocketNotifier::Read | eSocketNotifier::Priority | eSocketNotifier::Write);
439         }
440
441         return res;
442 }
443
444 void eDVBCISlot::data(int what)
445 {
446         if(what == eSocketNotifier::Priority) {
447                 if(state != stateRemoved) {
448                         state = stateRemoved;
449                         printf("ci removed\n");
450                         while(sendqueue.size())
451                         {
452                                 delete [] sendqueue.top().data;
453                                 sendqueue.pop();
454                         }
455                         eDVBCIInterfaces::getInstance()->ciRemoved(this);
456                         eDVBCISession::deleteSessions(this);
457                         notifier->setRequested(eSocketNotifier::Read);
458                         eDVBCI_UI::getInstance()->setState(getSlotID(),0);
459                 }
460                 return;
461         }
462
463         if (state == stateInvalid)
464                 reset();
465
466         if(state != stateInserted) {
467                 eDebug("ci inserted");
468                 state = stateInserted;
469                 eDVBCI_UI::getInstance()->setState(getSlotID(),1);
470                 notifier->setRequested(eSocketNotifier::Read|eSocketNotifier::Priority);
471                 /* enable PRI to detect removal or errors */
472         }
473
474         if (what & eSocketNotifier::Read) {
475                 __u8 data[4096];
476                 int r;
477                 r = ::read(fd, data, 4096);
478                 if(r > 0) {
479 //                      int i;
480 //                      printf("> ");
481 //                      for(i=0;i<r;i++)
482 //                              printf("%02x ",data[i]);
483 //                      printf("\n");
484                         eDVBCISession::receiveData(this, data, r);
485                         eDVBCISession::pollAll();
486                         return;
487                 }
488         }
489         else if (what & eSocketNotifier::Write) {
490                 if (!sendqueue.empty()) {
491                         const queueData &qe = sendqueue.top();
492                         int res = ::write(fd, qe.data, qe.len);
493                         if (res >= 0 && (unsigned int)res == qe.len)
494                         {
495                                 delete [] qe.data;
496                                 sendqueue.pop();
497                         }
498                 }
499                 else
500                         notifier->setRequested(eSocketNotifier::Read|eSocketNotifier::Priority);
501         }
502 }
503
504 DEFINE_REF(eDVBCISlot);
505
506 eDVBCISlot::eDVBCISlot(eMainloop *context, int nr)
507 {
508         char filename[128];
509
510         application_manager = 0;
511         mmi_session = 0;
512         ca_manager = 0;
513         use_count = 0;
514         
515         slotid = nr;
516
517         sprintf(filename, "/dev/ci%d", nr);
518
519         fd = ::open(filename, O_RDWR | O_NONBLOCK);
520
521         eDebug("eDVBCISlot has fd %d", fd);
522         state = stateInvalid;
523
524         if (fd >= 0)
525         {
526                 notifier = new eSocketNotifier(context, fd, eSocketNotifier::Read | eSocketNotifier::Priority | eSocketNotifier::Write);
527                 CONNECT(notifier->activated, eDVBCISlot::data);
528         } else
529         {
530                 perror(filename);
531         }
532
533         enableTS(0, 0);
534 }
535
536 eDVBCISlot::~eDVBCISlot()
537 {
538 }
539
540 void eDVBCISlot::setAppManager( eDVBCIApplicationManagerSession *session )
541 {
542         application_manager=session;
543 }
544
545 void eDVBCISlot::setMMIManager( eDVBCIMMISession *session )
546 {
547         mmi_session = session;
548 }
549
550 void eDVBCISlot::setCAManager( eDVBCICAManagerSession *session )
551 {
552         ca_manager = session;
553 }
554
555 int eDVBCISlot::getSlotID()
556 {
557         return slotid;
558 }
559
560 int eDVBCISlot::reset()
561 {
562         printf("edvbcislot: reset requested\n");
563
564         if (state == stateInvalid)
565         {
566                 unsigned char buf[256];
567                 eDebug("ci flush");
568                 while(::read(fd, buf, 256)>0);
569                 state = stateResetted;
570         }
571
572         while(sendqueue.size())
573         {
574                 delete [] sendqueue.top().data;
575                 sendqueue.pop();
576         }
577
578         ioctl(fd, 0);
579
580         return 0;
581 }
582
583 int eDVBCISlot::startMMI()
584 {
585         printf("edvbcislot: startMMI()\n");
586         
587         if(application_manager)
588                 application_manager->startMMI();
589         
590         return 0;
591 }
592
593 int eDVBCISlot::stopMMI()
594 {
595         printf("edvbcislot: stopMMI()\n");
596
597         if(mmi_session)
598                 mmi_session->stopMMI();
599         
600         return 0;
601 }
602
603 int eDVBCISlot::answerText(int answer)
604 {
605         printf("edvbcislot: answerText(%d)\n", answer);
606
607         if(mmi_session)
608                 mmi_session->answerText(answer);
609
610         return 0;
611 }
612
613 int eDVBCISlot::getMMIState()
614 {
615         if(mmi_session)
616                 return 1;
617
618         return 0;
619 }
620
621 int eDVBCISlot::answerEnq(char *value)
622 {
623         printf("edvbcislot: answerENQ(%s)\n", value);
624
625         if(mmi_session)
626                 mmi_session->answerEnq(value);
627
628         return 0;
629 }
630
631 int eDVBCISlot::cancelEnq()
632 {
633         printf("edvbcislot: cancelENQ\n");
634
635         if(mmi_session)
636                 mmi_session->cancelEnq();
637
638         return 0;
639 }
640
641 int eDVBCISlot::sendCAPMT(eDVBServicePMTHandler *pmthandler, const std::vector<uint16_t> &ids)
642 {
643         if (!ca_manager)
644         {
645                 eDebug("no ca_manager (no CI plugged?)");
646                 return -1;
647         }
648         const std::vector<uint16_t> &caids = ids.empty() ? ca_manager->getCAIDs() : ids;
649         ePtr<eTable<ProgramMapSection> > ptr;
650         if (pmthandler->getPMT(ptr))
651                 return -1;
652         else
653         {
654                 eDVBTableSpec table_spec;
655                 ptr->getSpec(table_spec);
656                 int pmt_version = table_spec.version & 0x1F; // just 5 bits
657
658                 eServiceReferenceDVB ref;
659                 pmthandler->getServiceReference(ref);
660                 uint16_t program_number = ref.getServiceID().get();
661                 std::map<uint16_t, uint8_t>::iterator it =
662                         running_services.find(program_number);
663
664                 if ( it != running_services.end() &&
665                         (pmt_version == it->second) &&
666                         !(caids.size() == 1 && caids[0] == 0xFFFF) )
667                 {
668                         eDebug("[eDVBCISlot] dont sent self capmt version twice");
669                         return -1;
670                 }
671
672                 std::vector<ProgramMapSection*>::const_iterator i=ptr->getSections().begin();
673                 if ( i == ptr->getSections().end() )
674                         return -1;
675                 else
676                 {
677                         unsigned char raw_data[2048];
678
679 //                      eDebug("send %s capmt for service %04x",
680 //                              it != running_services.end() ? "UPDATE" : running_services.empty() ? "ONLY" : "ADD",
681 //                              program_number);
682
683                         CaProgramMapSection capmt(*i++,
684                                 it != running_services.end() ? 0x05 /*update*/ : running_services.empty() ? 0x03 /*only*/ : 0x04 /*add*/, 0x01, caids );
685                         while( i != ptr->getSections().end() )
686                         {
687                 //                      eDebug("append");
688                                 capmt.append(*i++);
689                         }
690                         capmt.writeToBuffer(raw_data);
691 #if 1
692 // begin calc capmt length
693                         int wp=0;
694                         int hlen;
695                         if ( raw_data[3] & 0x80 )
696                         {
697                                 int i=0;
698                                 int lenbytes = raw_data[3] & ~0x80;
699                                 while(i < lenbytes)
700                                         wp = (wp << 8) | raw_data[4 + i++];
701                                 wp+=4;
702                                 wp+=lenbytes;
703                                 hlen = 4 + lenbytes;
704                         }
705                         else
706                         {
707                                 wp = raw_data[3];
708                                 wp+=4;
709                                 hlen = 4;
710                         }
711 // end calc capmt length
712 //                      eDebug("ca_manager %p dump capmt:", ca_manager);
713 //                      for(int i=0;i<wp;i++)
714 //                              eDebugNoNewLine("%02x ", raw_data[i]);
715 //                      eDebug("");
716 #endif
717                         if (caids.size() == 1 && caids[0] == 0xFFFF)
718                         {
719 //                              eDebugNoNewLine("SEND EMPTY CAPMT.. old version is %02x", raw_data[hlen+3]);
720                                 raw_data[hlen+3] &= ~0x3E;
721                                 raw_data[hlen+3] |= ((pmt_version+1) & 0x1F) << 1;
722 //                              eDebug(" new version is %02x", raw_data[hlen+3]);
723                         }
724
725                         //dont need tag and lenfield
726                         ca_manager->sendCAPMT(raw_data + hlen, wp - hlen);
727                         running_services[program_number] = pmt_version;
728                 }
729         }
730         return 0;
731 }
732
733 void eDVBCISlot::removeService(uint16_t program_number)
734 {
735         if (program_number == 0xFFFF)
736                 running_services.clear();  // remove all
737         else
738                 running_services.erase(program_number);  // remove single service
739 }
740
741 int eDVBCISlot::enableTS(int enable, int tuner)
742 {
743 //      printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
744 //      printf("eDVBCISlot::enableTS(%d %d)\n", enable, tuner);
745
746         FILE *input0, *input1, *ci;
747         if((input0 = fopen("/proc/stb/tsmux/input0", "wb")) == NULL) {
748                 printf("cannot open /proc/stb/tsmux/input0\n");
749                 return 0;
750         }
751         if((input1 = fopen("/proc/stb/tsmux/input1", "wb")) == NULL) {
752                 printf("cannot open /proc/stb/tsmux/input1\n");
753                 return 0;
754         }
755         if((ci = fopen("/proc/stb/tsmux/input2", "wb")) == NULL) {
756                 printf("cannot open /proc/stb/tsmux/input2\n");
757                 return 0;
758         }
759
760         fprintf(ci, "%s", tuner==0 ? "A" : "B");  // configure CI data source (TunerA, TunerB)
761         fprintf(input0, "%s", tuner==0 && enable ? "CI" : "A"); // configure ATI input 0 data source
762         fprintf(input1, "%s", tuner==1 && enable ? "CI" : "B"); // configure ATI input 1 data source
763
764         fclose(input0);
765         fclose(input1);
766         fclose(ci);
767         return 0;
768 }
769
770 eAutoInitP0<eDVBCIInterfaces> init_eDVBCIInterfaces(eAutoInitNumbers::dvb, "CI Slots");