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