differentiate between failures while and after tuning
[vuplus_dvbapp] / lib / dvb / dvb.cpp
1 #include <lib/base/eerror.h>
2 #include <lib/base/filepush.h>
3 #include <lib/dvb/idvb.h>
4 #include <lib/dvb/dvb.h>
5 #include <lib/dvb/sec.h>
6
7 #include <errno.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11 #include <fcntl.h>
12
13 DEFINE_REF(eDVBRegisteredFrontend);
14 DEFINE_REF(eDVBRegisteredDemux);
15
16 DEFINE_REF(eDVBAllocatedFrontend);
17
18 eDVBAllocatedFrontend::eDVBAllocatedFrontend(eDVBRegisteredFrontend *fe): m_fe(fe)
19 {
20         m_fe->m_inuse++;
21 }
22
23 eDVBAllocatedFrontend::~eDVBAllocatedFrontend()
24 {
25         --m_fe->m_inuse;
26 }
27
28 DEFINE_REF(eDVBAllocatedDemux);
29
30 eDVBAllocatedDemux::eDVBAllocatedDemux(eDVBRegisteredDemux *demux): m_demux(demux)
31 {
32         m_demux->m_inuse++;
33 }
34
35 eDVBAllocatedDemux::~eDVBAllocatedDemux()
36 {
37         --m_demux->m_inuse;
38 }
39
40 DEFINE_REF(eDVBResourceManager);
41
42 eDVBResourceManager *eDVBResourceManager::instance;
43
44 eDVBResourceManager::eDVBResourceManager()
45 {
46         avail = 1;
47         busy = 0;
48         m_sec = new eDVBSatelliteEquipmentControl;
49         if (!instance)
50                 instance = this;
51                 
52                 /* search available adapters... */
53
54                 // add linux devices
55         
56         int num_adapter = 0;
57         while (eDVBAdapterLinux::exist(num_adapter))
58         {
59                 addAdapter(new eDVBAdapterLinux(num_adapter));
60                 num_adapter++;
61         }
62         
63         eDebug("found %d adapter, %d frontends and %d demux", 
64                 m_adapter.size(), m_frontend.size(), m_demux.size());
65 }
66
67
68 DEFINE_REF(eDVBAdapterLinux);
69 eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr)
70 {
71                 // scan frontends
72         int num_fe = 0;
73         
74         eDebug("scanning for frontends..");
75         while (1)
76         {
77                 struct stat s;
78                 char filename[128];
79 #if HAVE_DVB_API_VERSION < 3
80                 sprintf(filename, "/dev/dvb/card%d/frontend%d", m_nr, num_fe);
81 #else
82                 sprintf(filename, "/dev/dvb/adapter%d/frontend%d", m_nr, num_fe);
83 #endif
84                 if (stat(filename, &s))
85                         break;
86                 ePtr<eDVBFrontend> fe;
87
88                 int ok = 0;
89                 fe = new eDVBFrontend(m_nr, num_fe, ok);
90                 if (ok)
91                         m_frontend.push_back(fe);
92                 ++num_fe;
93         }
94         
95                 // scan demux
96         int num_demux = 0;
97         while (1)
98         {
99                 struct stat s;
100                 char filename[128];
101 #if HAVE_DVB_API_VERSION < 3
102                 sprintf(filename, "/dev/dvb/card%d/demux%d", m_nr, num_demux);
103 #else
104                 sprintf(filename, "/dev/dvb/adapter%d/demux%d", m_nr, num_demux);
105 #endif
106                 if (stat(filename, &s))
107                         break;
108                 ePtr<eDVBDemux> demux;
109                 
110                 demux = new eDVBDemux(m_nr, num_demux);
111                 m_demux.push_back(demux);
112                         
113                 ++num_demux;
114         }
115 }
116
117 int eDVBAdapterLinux::getNumDemux()
118 {
119         return m_demux.size();
120 }
121
122 RESULT eDVBAdapterLinux::getDemux(ePtr<eDVBDemux> &demux, int nr)
123 {
124         eSmartPtrList<eDVBDemux>::iterator i(m_demux.begin());
125         while (nr && (i != m_demux.end()))
126         {
127                 --nr;
128                 ++i;
129         }
130         
131         if (i != m_demux.end())
132                 demux = *i;
133         else
134                 return -1;
135                 
136         return 0;
137 }
138
139 int eDVBAdapterLinux::getNumFrontends()
140 {
141         return m_frontend.size();
142 }
143
144 RESULT eDVBAdapterLinux::getFrontend(ePtr<eDVBFrontend> &fe, int nr)
145 {
146         eSmartPtrList<eDVBFrontend>::iterator i(m_frontend.begin());
147         while (nr && (i != m_frontend.end()))
148         {
149                 --nr;
150                 ++i;
151         }
152         
153         if (i != m_frontend.end())
154                 fe = *i;
155         else
156                 return -1;
157                 
158         return 0;
159 }
160
161 int eDVBAdapterLinux::exist(int nr)
162 {
163         struct stat s;
164         char filename[128];
165 #if HAVE_DVB_API_VERSION < 3
166         sprintf(filename, "/dev/dvb/card%d", nr);
167 #else
168         sprintf(filename, "/dev/dvb/adapter%d", nr);
169 #endif
170         if (!stat(filename, &s))
171                 return 1;
172         return 0;
173 }
174
175 eDVBResourceManager::~eDVBResourceManager()
176 {
177         if (instance == this)
178                 instance = 0;
179 }
180
181 void eDVBResourceManager::addAdapter(iDVBAdapter *adapter)
182 {
183         int num_fe = adapter->getNumFrontends();
184         int num_demux = adapter->getNumDemux();
185         
186         m_adapter.push_back(adapter);
187         
188         int i;
189         for (i=0; i<num_demux; ++i)
190         {
191                 ePtr<eDVBDemux> demux;
192                 if (!adapter->getDemux(demux, i))
193                         m_demux.push_back(new eDVBRegisteredDemux(demux, adapter));
194         }
195
196         for (i=0; i<num_fe; ++i)
197         {
198                 ePtr<eDVBFrontend> frontend;
199                 if (!adapter->getFrontend(frontend, i))
200                         m_frontend.push_back(new eDVBRegisteredFrontend(frontend, adapter));
201         }
202 }
203
204 RESULT eDVBResourceManager::allocateFrontend(const eDVBChannelID &chid, ePtr<eDVBAllocatedFrontend> &fe)
205 {
206                 /* find first unused frontend. we ignore compatibility for now. */
207         for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
208                 if (!i->m_inuse)
209                 {
210                         fe = new eDVBAllocatedFrontend(i);
211                         return 0;
212                 }
213         return -1;
214 }
215
216 RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBAllocatedDemux> &demux, int cap)
217 {
218                 /* find first unused demux which is on same adapter as frontend (or any, if PVR)
219                    never use the first one unless we need a decoding demux. */
220
221         eDebug("allocate demux");
222         eSmartPtrList<eDVBRegisteredDemux>::iterator i(m_demux.begin());
223         
224         if (i == m_demux.end())
225                 return -1;
226                 
227                 /* FIXME: hardware demux policy */
228         if (!(cap & iDVBChannel::capDecode))
229                 ++i;
230         
231         for (; i != m_demux.end(); ++i)
232                 if ((!i->m_inuse) && ((!fe) || (i->m_adapter == fe->m_adapter)))
233                 {
234                         demux = new eDVBAllocatedDemux(i);
235                         eDebug("demux found");
236                         return 0;
237                 }
238         eDebug("demux not found");
239         return -1;
240 }
241
242 RESULT eDVBResourceManager::setChannelList(iDVBChannelList *list)
243 {
244         m_list = list;
245         return 0;
246 }
247
248 RESULT eDVBResourceManager::getChannelList(ePtr<iDVBChannelList> &list)
249 {
250         list = m_list;
251         if (list)
252                 return 0;
253         else
254                 return -ENOENT;
255 }
256
257
258 RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUsePtr<iDVBChannel> &channel)
259 {
260                 /* first, check if a channel is already existing. */
261         
262 //      eDebug("allocate channel.. %04x:%04x", channelid.transport_stream_id.get(), channelid.original_network_id.get());
263         for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end(); ++i)
264         {
265 //              eDebug("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
266                 if (i->m_channel_id == channelid)
267                 {
268 //                      eDebug("found shared channel..");
269                         channel = i->m_channel;
270                         return 0;
271                 }
272         }
273         
274                 /* no currently available channel is tuned to this channelid. create a new one, if possible. */
275                 
276                 /* allocate a frontend. */
277         
278         ePtr<eDVBAllocatedFrontend> fe;
279         
280         if (allocateFrontend(channelid, fe))
281                 return errNoFrontend;
282         
283 // will be allocated on demand:
284 //      ePtr<eDVBAllocatedDemux> demux;
285 //      
286 //      if (allocateDemux(*fe, demux))
287 //              return errNoDemux;
288         
289         RESULT res;
290         ePtr<eDVBChannel> ch;
291         ch = new eDVBChannel(this, fe);
292
293         ePtr<iDVBFrontend> myfe;
294         if (!ch->getFrontend(myfe))
295                 myfe->setSEC(m_sec);
296
297         res = ch->setChannel(channelid);
298         if (res)
299         {
300                 channel = 0;
301                 return errChidNotFound;
302         }
303         
304         channel = ch;
305         return 0;
306 }
307
308 RESULT eDVBResourceManager::allocateRawChannel(eUsePtr<iDVBChannel> &channel)
309 {
310         ePtr<eDVBAllocatedFrontend> fe;
311         
312         if (allocateFrontend(eDVBChannelID(), fe))
313                 return errNoFrontend;
314         
315 //      ePtr<eDVBAllocatedDemux> demux;
316         //
317 //      if (allocateDemux(*fe, demux))
318 //              return errNoDemux;
319         
320         eDVBChannel *ch;
321         ch = new eDVBChannel(this, fe);
322
323         ePtr<iDVBFrontend> myfe;
324         if (!ch->getFrontend(myfe))
325                 myfe->setSEC(m_sec);
326
327         channel = ch;
328         return 0;
329 }
330
331
332 RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr<iDVBPVRChannel> &channel)
333 {
334         ePtr<eDVBAllocatedDemux> demux;
335         
336 //      if (allocateDemux(0, demux))
337 //              return errNoDemux;
338         
339         eDVBChannel *ch;
340         ch = new eDVBChannel(this, 0);
341         
342         channel = ch;
343         return 0;
344 }
345
346 RESULT eDVBResourceManager::addChannel(const eDVBChannelID &chid, eDVBChannel *ch)
347 {
348         m_active_channels.push_back(active_channel(chid, ch));
349         /* emit */ m_channelAdded(ch);
350         return 0;
351 }
352
353 RESULT eDVBResourceManager::removeChannel(eDVBChannel *ch)
354 {
355         int cnt = 0;
356         for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end();)
357         {
358                 if (i->m_channel == ch)
359                 {
360                         i = m_active_channels.erase(i);
361                         ++cnt;
362                 } else
363                         ++i;
364         }
365         ASSERT(cnt == 1);
366         if (cnt == 1)
367                 return 0;
368         return -ENOENT;
369 }
370
371 RESULT eDVBResourceManager::connectChannelAdded(const Slot1<void,eDVBChannel*> &channelAdded, ePtr<eConnection> &connection)
372 {
373         connection = new eConnection((eDVBResourceManager*)this, m_channelAdded.connect(channelAdded));
374         return 0;
375 }
376
377 DEFINE_REF(eDVBChannel);
378
379 eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, eDVBAllocatedFrontend *frontend): m_state(state_idle), m_mgr(mgr)
380 {
381         m_frontend = frontend;
382
383         m_pvr_thread = 0;
384         
385         if (m_frontend)
386                 m_frontend->get().connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged);
387 }
388
389 eDVBChannel::~eDVBChannel()
390 {
391         if (m_channel_id)
392                 m_mgr->removeChannel(this);
393         
394         if (m_pvr_thread)
395         {
396                 m_pvr_thread->stop();
397                 ::close(m_pvr_fd_src);
398                 ::close(m_pvr_fd_dst);
399                 delete m_pvr_thread;
400         }
401 }
402
403 void eDVBChannel::frontendStateChanged(iDVBFrontend*fe)
404 {
405         int state, ourstate = 0;
406         
407                 /* if we are already in shutdown, don't change state. */
408         if (m_state == state_release)
409                 return;
410         
411         if (fe->getState(state))
412                 return;
413         
414         if (state == iDVBFrontend::stateLock)
415         {
416                 eDebug("OURSTATE: ok");
417                 ourstate = state_ok;
418         } else if (state == iDVBFrontend::stateTuning)
419         {
420                 eDebug("OURSTATE: tuning");
421                 ourstate = state_tuning;
422         } else if (state == iDVBFrontend::stateLostLock)
423         {
424                 eDebug("OURSTATE: lost lock");
425                 ourstate = state_unavailable;
426         } else if (state == iDVBFrontend::stateFailed)
427         {
428                 eDebug("OURSTATE: failed");
429                 ourstate = state_failed;
430         } else
431                 eFatal("state unknown");
432         
433         if (ourstate != m_state)
434         {
435                 m_state = ourstate;
436                 m_stateChanged(this);
437         }
438 }
439
440 void eDVBChannel::AddUse()
441 {
442         ++m_use_count;
443 }
444
445 void eDVBChannel::ReleaseUse()
446 {
447         if (!--m_use_count)
448         {
449                 m_state = state_release;
450                 m_stateChanged(this);
451         }
452 }
453
454 RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid)
455 {
456         if (m_channel_id)
457                 m_mgr->removeChannel(this);
458                 
459         if (!channelid)
460                 return 0;
461
462         ePtr<iDVBChannelList> list;
463         
464         if (m_mgr->getChannelList(list))
465         {
466                 eDebug("no channel list set!");
467                 return -ENOENT;
468         }
469         
470         eDebug("tuning to chid: ns: %08x tsid %04x onid %04x",
471                 channelid.dvbnamespace.get(), channelid.transport_stream_id.get(), channelid.original_network_id.get());
472
473         ePtr<iDVBFrontendParameters> feparm;
474         if (list->getChannelFrontendData(channelid, feparm))
475         {
476                 eDebug("channel not found!");
477                 return -ENOENT;
478         }
479         
480         if (!m_frontend)
481         {
482                 eDebug("no frontend to tune!");
483                 return -ENODEV;
484         }
485         
486         m_channel_id = channelid;
487         m_mgr->addChannel(channelid, this);
488         m_state = state_tuning;
489         return m_frontend->get().tune(*feparm);
490 }
491
492 RESULT eDVBChannel::connectStateChange(const Slot1<void,iDVBChannel*> &stateChange, ePtr<eConnection> &connection)
493 {
494         connection = new eConnection((iDVBChannel*)this, m_stateChanged.connect(stateChange));
495         return 0;
496 }
497
498 RESULT eDVBChannel::getState(int &state)
499 {
500         state = m_state;
501         return 0;
502 }
503
504 RESULT eDVBChannel::setCIRouting(const eDVBCIRouting &routing)
505 {
506         return -1;
507 }
508
509 RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux, int cap)
510 {
511         ePtr<eDVBAllocatedDemux> &our_demux = (cap & capDecode) ? m_decoder_demux : m_demux;
512         
513         if (!our_demux)
514         {
515                 demux = 0;
516                 
517                 if (m_mgr->allocateDemux(m_frontend ? (eDVBRegisteredFrontend*)*m_frontend : (eDVBRegisteredFrontend*)0, our_demux, cap))
518                         return 0;
519         }
520         
521         demux = *our_demux;
522         return 0;
523 }
524
525 RESULT eDVBChannel::getFrontend(ePtr<iDVBFrontend> &frontend)
526 {
527         frontend = &m_frontend->get();
528         if (frontend)
529                 return 0;
530         else
531                 return -ENODEV;
532 }
533
534 RESULT eDVBChannel::playFile(const char *file)
535 {
536         ASSERT(!m_frontend);
537         if (m_pvr_thread)
538         {
539                 m_pvr_thread->stop();
540                 delete m_pvr_thread;
541                 m_pvr_thread = 0;
542         }
543         
544         m_tstools.openFile(file);
545         
546                 /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST,
547                    THEN DO A REAL FIX HERE! */
548         
549                 /* (this codepath needs to be improved anyway.) */
550         m_pvr_fd_dst = open("/dev/misc/pvr", O_WRONLY);
551         if (m_pvr_fd_dst < 0)
552         {
553                 eDebug("can't open /dev/misc/pvr - you need to buy the new(!) $$$ box! (%m)");
554                 return -ENODEV;
555         }
556         
557         m_pvr_fd_src = open(file, O_RDONLY);
558         if (m_pvr_fd_src < 0)
559         {
560                 eDebug("can't open PVR m_pvr_fd_src file %s (%m)", file);
561                 close(m_pvr_fd_dst);
562                 return -ENOENT;
563         }
564         
565         m_state = state_ok;
566         m_stateChanged(this);
567         
568         m_pvr_thread = new eFilePushThread();
569         m_pvr_thread->start(m_pvr_fd_src, m_pvr_fd_dst);
570
571         return 0;
572 }
573
574 RESULT eDVBChannel::getLength(pts_t &len)
575 {
576         return m_tstools.calcLen(len);
577 }
578
579 RESULT eDVBChannel::getCurrentPosition(pts_t &pos)
580 {
581         if (!m_decoder_demux)
582                 return -1;
583         
584         off_t begin = 0;
585                 /* getPTS for offset 0 is cached, so it doesn't harm. */
586         int r = m_tstools.getPTS(begin, pos);
587         if (r)
588         {
589                 eDebug("tstools getpts(0) failed!");
590                 return r;
591         }
592         
593         pts_t now;
594         
595         r = m_decoder_demux->get().getSTC(now);
596
597         if (r)
598         {
599                 eDebug("demux getSTC failed");
600                 return -1;
601         }
602         
603 //      eDebug("STC: %08llx PTS: %08llx, diff %lld", now, pos, now - pos);
604                 /* when we are less than 10 seconds before the start, return 0. */
605                 /* (we're just waiting for the timespam to start) */
606         if ((now < pos) && ((pos - now) < 90000 * 10))
607         {
608                 pos = 0;
609                 return 0;
610         }
611         
612         if (now < pos) /* wrap around */
613                 pos = now + ((pts_t)1)<<33 - pos;
614         else
615                 pos = now - pos;
616         
617         return 0;
618 }
619
620 RESULT eDVBChannel::seekTo(pts_t &pts)
621 {
622         m_pvr_thread->pause();
623         if (m_decoder_demux)
624                 m_decoder_demux->get().flush();
625                 /* demux will also flush all decoder.. */
626 //      m_pvr_thread->seek(pts);
627         m_pvr_thread->resume();
628 }
629