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