dont reset the "service new found flag" in sdt scan
[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)
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                         eDVBCI_UI::getInstance()->setState(getSlotID(),0);
441                 }
442                 return;
443         }
444
445         if(state != stateInserted) {
446                 eDebug("ci inserted");
447                 state = stateInserted;
448                 eDVBCI_UI::getInstance()->setState(getSlotID(),1);
449                 notifier->setRequested(eSocketNotifier::Read|eSocketNotifier::Priority);
450                 /* enable PRI to detect removal or errors */
451         }
452
453         if (what & eSocketNotifier::Read) {
454                 __u8 data[4096];
455                 int r;
456                 r = ::read(fd, data, 4096);
457                 if(r > 0) {
458 //                      int i;
459 //                      printf("> ");
460 //                      for(i=0;i<r;i++)
461 //                              printf("%02x ",data[i]);
462 //                      printf("\n");
463                         eDVBCISession::receiveData(this, data, r);
464                         eDVBCISession::pollAll();
465                         return;
466                 }
467         }
468         else if (what & eSocketNotifier::Write) {
469                 if (!sendqueue.empty()) {
470                         const queueData &qe = sendqueue.top();
471                         int res = ::write(fd, qe.data, qe.len);
472                         if (res >= 0 && (unsigned int)res == qe.len)
473                         {
474                                 delete [] qe.data;
475                                 sendqueue.pop();
476                         }
477                 }
478                 else
479                         notifier->setRequested(eSocketNotifier::Read|eSocketNotifier::Priority);
480         }
481 }
482
483 DEFINE_REF(eDVBCISlot);
484
485 eDVBCISlot::eDVBCISlot(eMainloop *context, int nr)
486 {
487         char filename[128];
488
489         application_manager = 0;
490         mmi_session = 0;
491         ca_manager = 0;
492         use_count = 0;
493         
494         slotid = nr;
495
496         sprintf(filename, "/dev/ci%d", nr);
497
498         fd = ::open(filename, O_RDWR | O_NONBLOCK);
499
500         eDebug("eDVBCISlot has fd %d", fd);
501         state = stateInvalid;
502
503         if (fd >= 0)
504         {
505                 notifier = new eSocketNotifier(context, fd, eSocketNotifier::Read | eSocketNotifier::Priority | eSocketNotifier::Write);
506                 CONNECT(notifier->activated, eDVBCISlot::data);
507         } else
508         {
509                 perror(filename);
510         }
511
512         enableTS(0, 0);
513 }
514
515 eDVBCISlot::~eDVBCISlot()
516 {
517 }
518
519 void eDVBCISlot::setAppManager( eDVBCIApplicationManagerSession *session )
520 {
521         application_manager=session;
522 }
523
524 void eDVBCISlot::setMMIManager( eDVBCIMMISession *session )
525 {
526         mmi_session = session;
527 }
528
529 void eDVBCISlot::setCAManager( eDVBCICAManagerSession *session )
530 {
531         ca_manager = session;
532 }
533
534 int eDVBCISlot::getSlotID()
535 {
536         return slotid;
537 }
538
539 int eDVBCISlot::reset()
540 {
541         printf("edvbcislot: reset requested\n");
542
543         if (state == stateInvalid)
544         {
545                 unsigned char buf[256];
546                 eDebug("ci flush");
547                 while(::read(fd, buf, 256)>0);
548                 state = stateResetted;
549         }
550
551         while(sendqueue.size())
552         {
553                 delete [] sendqueue.top().data;
554                 sendqueue.pop();
555         }
556
557         ioctl(fd, 0);
558
559         return 0;
560 }
561
562 int eDVBCISlot::startMMI()
563 {
564         printf("edvbcislot: startMMI()\n");
565         
566         if(application_manager)
567                 application_manager->startMMI();
568         
569         return 0;
570 }
571
572 int eDVBCISlot::stopMMI()
573 {
574         printf("edvbcislot: stopMMI()\n");
575
576         if(mmi_session)
577                 mmi_session->stopMMI();
578         
579         return 0;
580 }
581
582 int eDVBCISlot::answerText(int answer)
583 {
584         printf("edvbcislot: answerText(%d)\n", answer);
585
586         if(mmi_session)
587                 mmi_session->answerText(answer);
588
589         return 0;
590 }
591
592 int eDVBCISlot::getMMIState()
593 {
594         if(mmi_session)
595                 return 1;
596
597         return 0;
598 }
599
600 int eDVBCISlot::answerEnq(char *value)
601 {
602         printf("edvbcislot: answerENQ(%s)\n", value);
603
604         if(mmi_session)
605                 mmi_session->answerEnq(value);
606
607         return 0;
608 }
609
610 int eDVBCISlot::cancelEnq()
611 {
612         printf("edvbcislot: cancelENQ\n");
613
614         if(mmi_session)
615                 mmi_session->cancelEnq();
616
617         return 0;
618 }
619
620 int eDVBCISlot::sendCAPMT(eDVBServicePMTHandler *pmthandler, const std::vector<uint16_t> &ids)
621 {
622         if (!ca_manager)
623         {
624                 eDebug("no ca_manager (no CI plugged?)");
625                 return -1;
626         }
627         const std::vector<uint16_t> &caids = ids.empty() ? ca_manager->getCAIDs() : ids;
628         ePtr<eTable<ProgramMapSection> > ptr;
629         if (pmthandler->getPMT(ptr))
630                 return -1;
631         else
632         {
633                 eDVBTableSpec table_spec;
634                 ptr->getSpec(table_spec);
635                 int pmt_version = table_spec.version & 0x1F; // just 5 bits
636
637                 eServiceReferenceDVB ref;
638                 pmthandler->getServiceReference(ref);
639                 uint16_t program_number = ref.getServiceID().get();
640                 std::map<uint16_t, uint8_t>::iterator it =
641                         running_services.find(program_number);
642
643                 if ( it != running_services.end() &&
644                         (pmt_version == it->second) &&
645                         !(caids.size() == 1 && caids[0] == 0xFFFF) )
646                 {
647                         eDebug("[eDVBCISlot] dont sent self capmt version twice");
648                         return -1;
649                 }
650
651                 std::vector<ProgramMapSection*>::const_iterator i=ptr->getSections().begin();
652                 if ( i == ptr->getSections().end() )
653                         return -1;
654                 else
655                 {
656                         unsigned char raw_data[2048];
657
658 //                      eDebug("send %s capmt for service %04x",
659 //                              it != running_services.end() ? "UPDATE" : running_services.empty() ? "ONLY" : "ADD",
660 //                              program_number);
661
662                         CaProgramMapSection capmt(*i++,
663                                 it != running_services.end() ? 0x05 /*update*/ : running_services.empty() ? 0x03 /*only*/ : 0x04 /*add*/, 0x01, caids );
664                         while( i != ptr->getSections().end() )
665                         {
666                 //                      eDebug("append");
667                                 capmt.append(*i++);
668                         }
669                         capmt.writeToBuffer(raw_data);
670 #if 1
671 // begin calc capmt length
672                         int wp=0;
673                         int hlen;
674                         if ( raw_data[3] & 0x80 )
675                         {
676                                 int i=0;
677                                 int lenbytes = raw_data[3] & ~0x80;
678                                 while(i < lenbytes)
679                                         wp = (wp << 8) | raw_data[4 + i++];
680                                 wp+=4;
681                                 wp+=lenbytes;
682                                 hlen = 4 + lenbytes;
683                         }
684                         else
685                         {
686                                 wp = raw_data[3];
687                                 wp+=4;
688                                 hlen = 4;
689                         }
690 // end calc capmt length
691 //                      eDebug("ca_manager %p dump capmt:", ca_manager);
692 //                      for(int i=0;i<wp;i++)
693 //                              eDebugNoNewLine("%02x ", raw_data[i]);
694 //                      eDebug("");
695 #endif
696                         if (caids.size() == 1 && caids[0] == 0xFFFF)
697                         {
698 //                              eDebugNoNewLine("SEND EMPTY CAPMT.. old version is %02x", raw_data[hlen+3]);
699                                 raw_data[hlen+3] &= ~0x3E;
700                                 raw_data[hlen+3] |= ((pmt_version+1) & 0x1F) << 1;
701 //                              eDebug(" new version is %02x", raw_data[hlen+3]);
702                         }
703
704                         //dont need tag and lenfield
705                         ca_manager->sendCAPMT(raw_data + hlen, wp - hlen);
706                         running_services[program_number] = pmt_version;
707                 }
708         }
709         return 0;
710 }
711
712 void eDVBCISlot::removeService(uint16_t program_number)
713 {
714         if (program_number == 0xFFFF)
715                 running_services.clear();  // remove all
716         else
717                 running_services.erase(program_number);  // remove single service
718 }
719
720 int eDVBCISlot::enableTS(int enable, int tuner)
721 {
722 //      printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
723 //      printf("eDVBCISlot::enableTS(%d %d)\n", enable, tuner);
724
725         FILE *input0, *input1, *ci;
726         if((input0 = fopen("/proc/stb/tsmux/input0", "wb")) == NULL) {
727                 printf("cannot open /proc/stb/tsmux/input0\n");
728                 return 0;
729         }
730         if((input1 = fopen("/proc/stb/tsmux/input1", "wb")) == NULL) {
731                 printf("cannot open /proc/stb/tsmux/input1\n");
732                 return 0;
733         }
734         if((ci = fopen("/proc/stb/tsmux/input2", "wb")) == NULL) {
735                 printf("cannot open /proc/stb/tsmux/input2\n");
736                 return 0;
737         }
738
739         fprintf(ci, "%s", tuner==0 ? "A" : "B");  // configure CI data source (TunerA, TunerB)
740         fprintf(input0, "%s", tuner==0 && enable ? "CI" : "A"); // configure ATI input 0 data source
741         fprintf(input1, "%s", tuner==1 && enable ? "CI" : "B"); // configure ATI input 1 data source
742
743         fclose(input0);
744         fclose(input1);
745         fclose(ci);
746         return 0;
747 }
748
749 eAutoInitP0<eDVBCIInterfaces> init_eDVBCIInterfaces(eAutoInitNumbers::dvb, "CI Slots");