fix memleaks (use smartpointers)
[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
78         return slot->reset();
79 }
80
81 int eDVBCIInterfaces::enableTS(int slotid, int enable)
82 {
83         eDVBCISlot *slot;
84
85         if( (slot = getSlot(slotid)) == 0 )
86                 return -1;
87
88         return slot->enableTS(enable);
89 }
90
91 int eDVBCIInterfaces::initialize(int slotid)
92 {
93         eDVBCISlot *slot;
94
95         if( (slot = getSlot(slotid)) == 0 )
96                 return -1;
97
98         slot->resetPrevSentCAPMTVersion();
99         PMTHandlerList::iterator it = m_pmt_handlers.begin();
100         while (it != m_pmt_handlers.end())
101         {
102                 if ( it->cislot == slot )
103                 {
104                         slot->sendCAPMT(it->pmthandler);  // send capmt
105                         break;
106                 }
107                 ++it;
108         }
109
110         return slot->initialize();
111 }
112
113 int eDVBCIInterfaces::sendCAPMT(int slotid)
114 {
115         eDVBCISlot *slot;
116
117         if( (slot = getSlot(slotid)) == 0 )
118                 return -1;
119
120         slot->resetPrevSentCAPMTVersion();
121         PMTHandlerList::iterator it = m_pmt_handlers.begin();
122         while (it != m_pmt_handlers.end())
123         {
124                 if ( it->cislot == slot )
125                 {
126                         slot->sendCAPMT(it->pmthandler);  // send capmt
127                         return 0;
128                 }
129                 ++it;
130         }
131
132         return -1;
133 }
134
135 int eDVBCIInterfaces::startMMI(int slotid)
136 {
137         eDVBCISlot *slot;
138
139         if( (slot = getSlot(slotid)) == 0 )
140                 return -1;
141         
142         return slot->startMMI();
143 }
144
145 int eDVBCIInterfaces::stopMMI(int slotid)
146 {
147         eDVBCISlot *slot;
148
149         if( (slot = getSlot(slotid)) == 0 )
150                 return -1;
151         
152         return slot->stopMMI();
153 }
154
155 int eDVBCIInterfaces::answerText(int slotid, int answer)
156 {
157         eDVBCISlot *slot;
158
159         if( (slot = getSlot(slotid)) == 0 )
160                 return -1;
161         
162         return slot->answerText(answer);
163 }
164
165 int eDVBCIInterfaces::answerEnq(int slotid, char *value)
166 {
167         eDVBCISlot *slot;
168
169         if( (slot = getSlot(slotid)) == 0 )
170                 return -1;
171         
172         return slot->answerEnq(value);
173 }
174
175 int eDVBCIInterfaces::cancelEnq(int slotid)
176 {
177         eDVBCISlot *slot;
178
179         if( (slot = getSlot(slotid)) == 0 )
180                 return -1;
181         
182         return slot->cancelEnq();
183 }
184
185 void eDVBCIInterfaces::addPMTHandler(eDVBServicePMTHandler *pmthandler)
186 {
187         CIPmtHandler new_handler(pmthandler);
188
189         eServiceReferenceDVB service;
190         pmthandler->getService(service);
191
192         eDebug("[eDVBCIInterfaces] addPMTHandler %s", service.toString().c_str());
193
194         // HACK the first service get the CI..
195         eSmartPtrList<eDVBCISlot>::iterator ci_it(m_slots.begin());
196         for (; ci_it != m_slots.end(); ++ci_it)
197         {
198                 if (ci_it->use_count)
199                         continue;
200                 ci_it->use_count=1;
201                 new_handler.cislot = ci_it;
202                 new_handler.cislot->resetPrevSentCAPMTVersion();
203         }
204
205         if (ci_it == m_slots.end())
206         {
207                 PMTHandlerList::iterator it = m_pmt_handlers.begin();
208                 while (it != m_pmt_handlers.end())
209                 {
210                         eServiceReferenceDVB ref;
211                         it->pmthandler->getService(ref);
212                         if ( service == ref && it->cislot )
213                         {
214                                 new_handler.cislot = it->cislot;
215                                 ++new_handler.cislot->use_count;
216                                 break;
217                         }
218                         ++it;
219                 }
220         }
221
222         m_pmt_handlers.push_back(new_handler);
223 }
224
225 void eDVBCIInterfaces::removePMTHandler(eDVBServicePMTHandler *pmthandler)
226 {
227         PMTHandlerList::iterator it=std::find(m_pmt_handlers.begin(),m_pmt_handlers.end(),pmthandler);
228         if (it != m_pmt_handlers.end())
229         {
230                 eDVBCISlot *slot = it->cislot;
231 //              eDVBServicePMTHandler *pmthandler = it->pmthandler;
232                 m_pmt_handlers.erase(it);
233                 if (slot && !--slot->use_count)
234                 {
235 #if 0
236                         eDebug("[eDVBCIInterfaces] remove last pmt handler for service %s send empty capmt");
237                         std::vector<uint16_t> caids;
238                         caids.push_back(0xFFFF);
239                         slot->resetPrevSentCAPMTVersion();
240                         slot->sendCAPMT(pmthandler, caids);
241 #endif
242         // check if another service is running
243                         it = m_pmt_handlers.begin();
244                         while (it != m_pmt_handlers.end())
245                         {
246                                 if ( !it->cislot )
247                                 {
248                                         it->cislot = slot;
249                                         ++slot->use_count;
250                                         slot->resetPrevSentCAPMTVersion();
251                                         slot->sendCAPMT(it->pmthandler);
252                                         break;
253                                 }
254                                 ++it;
255                         }
256                 }
257         }
258 }
259
260 void eDVBCIInterfaces::gotPMT(eDVBServicePMTHandler *pmthandler)
261 {
262         eDebug("[eDVBCIInterfaces] gotPMT");
263         PMTHandlerList::iterator it=std::find(m_pmt_handlers.begin(), m_pmt_handlers.end(), pmthandler);
264         eServiceReferenceDVB service;
265         if ( it != m_pmt_handlers.end() && it->cislot)
266                 it->cislot->sendCAPMT(pmthandler);
267 }
268
269 int eDVBCIInterfaces::getMMIState(int slotid)
270 {
271         eDVBCISlot *slot;
272
273         if( (slot = getSlot(slotid)) == 0 )
274                 return -1;
275         
276         return slot->getMMIState();
277 }
278
279 int eDVBCISlot::send(const unsigned char *data, size_t len)
280 {
281         int res;
282         //int i;
283         //printf("< ");
284         //for(i=0;i<len;i++)
285         //      printf("%02x ",data[i]);
286         //printf("\n");
287
288         res = ::write(fd, data, len);
289
290         //printf("write() %d\n",res);
291
292         notifier->setRequested(eSocketNotifier::Read | eSocketNotifier::Priority | eSocketNotifier::Write);
293
294         return res;
295 }
296
297 void eDVBCISlot::data(int what)
298 {
299         if(what == eSocketNotifier::Priority) {
300                 if(state != stateRemoved) {
301                         state = stateRemoved;
302                         enableTS(0);
303                         printf("ci removed\n");
304                         eDVBCISession::deleteSessions(this);
305                         notifier->setRequested(eSocketNotifier::Read);
306                         //HACK
307                         eDVBCI_UI::getInstance()->setState(0,0);
308                 }
309                 return;
310         }
311
312         __u8 data[4096];
313         int r;
314         r = ::read(fd, data, 4096);
315
316         if(state != stateInserted) {
317                 state = stateInserted;
318                 eDebug("ci inserted");
319
320                 //HACK
321                 eDVBCI_UI::getInstance()->setState(0,1);
322
323                 /* enable PRI to detect removal or errors */
324                 notifier->setRequested(eSocketNotifier::Read|eSocketNotifier::Priority|eSocketNotifier::Write);
325         }
326
327         if(r > 0) {
328                 //int i;
329                 //printf("> ");
330                 //for(i=0;i<r;i++)
331                 //      printf("%02x ",data[i]);
332                 //printf("\n");
333                 eDVBCISession::receiveData(this, data, r);
334                 notifier->setRequested(eSocketNotifier::Read|eSocketNotifier::Priority|eSocketNotifier::Write);
335                 return;
336         }
337
338         if(what == eSocketNotifier::Write) {
339                 if(eDVBCISession::pollAll() == 0) {
340                         notifier->setRequested(eSocketNotifier::Read | eSocketNotifier::Priority);
341                 }
342         }
343 }
344
345 DEFINE_REF(eDVBCISlot);
346
347 eDVBCISlot::eDVBCISlot(eMainloop *context, int nr)
348 {
349         char filename[128];
350
351         application_manager = 0;
352         mmi_session = 0;
353         ca_manager = 0;
354         use_count = 0;
355         
356         slotid = nr;
357
358         sprintf(filename, "/dev/ci%d", nr);
359
360         fd = ::open(filename, O_RDWR | O_NONBLOCK);
361
362         eDebug("eDVBCISlot has fd %d", fd);
363         
364         state = stateInserted;
365
366         if (fd >= 0)
367         {
368                 notifier = new eSocketNotifier(context, fd, eSocketNotifier::Read | eSocketNotifier::Priority);
369                 CONNECT(notifier->activated, eDVBCISlot::data);
370         } else
371         {
372                 perror(filename);
373         }
374 }
375
376 eDVBCISlot::~eDVBCISlot()
377 {
378         enableTS(0);
379 }
380
381 void eDVBCISlot::setAppManager( eDVBCIApplicationManagerSession *session )
382 {
383         application_manager=session;
384 }
385
386 void eDVBCISlot::setMMIManager( eDVBCIMMISession *session )
387 {
388         mmi_session = session;
389 }
390
391 void eDVBCISlot::setCAManager( eDVBCICAManagerSession *session )
392 {
393         ca_manager = session;
394 }
395
396 int eDVBCISlot::getSlotID()
397 {
398         return slotid;
399 }
400
401 int eDVBCISlot::reset()
402 {
403         printf("edvbcislot: reset requested\n");
404
405         enableTS(0);
406
407         ioctl(fd, 0);
408
409         return 0;
410 }
411
412 int eDVBCISlot::initialize()
413 {
414         printf("edvbcislot: initialize()\n");
415         return 0;
416 }
417
418 int eDVBCISlot::startMMI()
419 {
420         printf("edvbcislot: startMMI()\n");
421         
422         if(application_manager)
423                 application_manager->startMMI();
424         
425         return 0;
426 }
427
428 int eDVBCISlot::stopMMI()
429 {
430         printf("edvbcislot: stopMMI()\n");
431
432         if(mmi_session)
433                 mmi_session->stopMMI();
434         
435         return 0;
436 }
437
438 int eDVBCISlot::answerText(int answer)
439 {
440         printf("edvbcislot: answerText(%d)\n", answer);
441
442         if(mmi_session)
443                 mmi_session->answerText(answer);
444
445         return 0;
446 }
447
448 int eDVBCISlot::getMMIState()
449 {
450         if(mmi_session)
451                 return 1;
452
453         return 0;
454 }
455
456 int eDVBCISlot::answerEnq(char *value)
457 {
458         printf("edvbcislot: answerENQ(%s)\n", value);
459         return 0;
460 }
461
462 int eDVBCISlot::cancelEnq()
463 {
464         printf("edvbcislot: cancelENQ\n");
465
466         if(mmi_session)
467                 mmi_session->cancelEnq();
468
469         return 0;
470 }
471
472 int eDVBCISlot::sendCAPMT(eDVBServicePMTHandler *pmthandler, const std::vector<uint16_t> &ids)
473 {
474         if (!ca_manager)
475         {
476                 eDebug("no ca_manager (no CI plugged?)");
477                 return -1;
478         }
479         const std::vector<uint16_t> &caids = ids.empty() ? ca_manager->getCAIDs() : ids;
480         ePtr<eTable<ProgramMapSection> > ptr;
481         if (pmthandler->getPMT(ptr))
482                 return -1;
483         else
484         {
485                 eDVBTableSpec table_spec;
486                 ptr->getSpec(table_spec);
487                 int pmt_version = table_spec.version & 0x1F; // just 5 bits
488                 if ( pmt_version == prev_sent_capmt_version )
489                 {
490                         eDebug("[eDVBCISlot] dont sent self capmt version twice");
491                         return -1;
492                 }
493                 std::vector<ProgramMapSection*>::const_iterator i=ptr->getSections().begin();
494                 if ( i == ptr->getSections().end() )
495                         return -1;
496                 else
497                 {
498                         unsigned char raw_data[2048];
499                         CaProgramMapSection capmt(*i++, prev_sent_capmt_version != 0xFF ? 0x05 /*update*/ : 0x03 /*only*/, 0x01, caids );
500                         while( i != ptr->getSections().end() )
501                         {
502                 //                      eDebug("append");
503                                 capmt.append(*i++);
504                         }
505                         capmt.writeToBuffer(raw_data);
506 #if 1
507 // begin calc capmt length
508                         int wp=0;
509                         int hlen;
510                         if ( raw_data[3] & 0x80 )
511                         {
512                                 int i=0;
513                                 int lenbytes = raw_data[3] & ~0x80;
514                                 while(i < lenbytes)
515                                         wp = (wp << 8) | raw_data[4 + i++];
516                                 wp+=4;
517                                 wp+=lenbytes;
518                                 hlen = 4 + lenbytes;
519                         }
520                         else
521                         {
522                                 wp = raw_data[3];
523                                 wp+=4;
524                                 hlen = 4;
525                         }
526 // end calc capmt length
527                         eDebug("ca_manager %p dump capmt:", ca_manager);
528                         for(int i=0;i<wp;i++)
529                                 eDebugNoNewLine("%02x ", raw_data[i]);
530                         eDebug("");
531 #endif
532                         //dont need tag and lenfield
533                         ca_manager->sendCAPMT(raw_data + hlen, wp - hlen);
534                         prev_sent_capmt_version = pmt_version;
535                 }
536         }
537         return 0;
538 }
539
540 int eDVBCISlot::enableTS(int enable)
541 {
542         printf("eDVBCISlot::enableTS(%d)\n", enable);
543
544         FILE *f;
545         if((f = fopen("/proc/stb/tsmux/input0", "wb")) == NULL) {
546                 printf("cannot open /proc/stb/tsmux/input0\n");
547                 return 0;
548         }
549
550         fprintf(f, "%s", enable?"CI":"A");
551
552         fclose(f);
553
554         return 0;
555 }
556
557 void eDVBCISlot::resendCAPMT()
558 {
559         eDVBCIInterfaces::getInstance()->sendCAPMT(slotid);
560 }
561
562 eAutoInitP0<eDVBCIInterfaces> init_eDVBCIInterfaces(eAutoInitNumbers::dvb, "CI Slots");