- add iSeekableService, implement it for serviceDvb
[enigma2.git] / 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
182 void eDVBResourceManager::addAdapter(iDVBAdapter *adapter)
183 {
184         int num_fe = adapter->getNumFrontends();
185         int num_demux = adapter->getNumDemux();
186         
187         m_adapter.push_back(adapter);
188         
189         int i;
190         for (i=0; i<num_demux; ++i)
191         {
192                 ePtr<eDVBDemux> demux;
193                 if (!adapter->getDemux(demux, i))
194                         m_demux.push_back(new eDVBRegisteredDemux(demux, adapter));
195         }
196
197         for (i=0; i<num_fe; ++i)
198         {
199                 ePtr<eDVBFrontend> frontend;
200                 if (!adapter->getFrontend(frontend, i))
201                         m_frontend.push_back(new eDVBRegisteredFrontend(frontend, adapter));
202         }
203 }
204
205 RESULT eDVBResourceManager::allocateFrontend(const eDVBChannelID &chid, ePtr<eDVBAllocatedFrontend> &fe)
206 {
207                 /* find first unused frontend. we ignore compatibility for now. */
208         for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
209                 if (!i->m_inuse)
210                 {
211                         fe = new eDVBAllocatedFrontend(i);
212                         return 0;
213                 }
214         return -1;
215 }
216
217 RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBAllocatedDemux> &demux)
218 {
219                 /* find first unused demux which is on same adapter as frontend (or any, if PVR) */
220         for (eSmartPtrList<eDVBRegisteredDemux>::iterator i(m_demux.begin()); i != m_demux.end(); ++i)
221                 if ((!i->m_inuse) && ((!fe) || (i->m_adapter == fe->m_adapter)))
222                 {
223                         demux = new eDVBAllocatedDemux(i);
224                         return 0;
225                 }
226         return -1;
227 }
228
229 RESULT eDVBResourceManager::setChannelList(iDVBChannelList *list)
230 {
231         m_list = list;
232         return 0;
233 }
234
235 RESULT eDVBResourceManager::getChannelList(ePtr<iDVBChannelList> &list)
236 {
237         list = m_list;
238         if (list)
239                 return 0;
240         else
241                 return -ENOENT;
242 }
243
244
245 RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUsePtr<iDVBChannel> &channel)
246 {
247                 /* first, check if a channel is already existing. */
248         
249 //      eDebug("allocate channel.. %04x:%04x", channelid.transport_stream_id.get(), channelid.original_network_id.get());
250         for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end(); ++i)
251         {
252 //              eDebug("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
253                 if (i->m_channel_id == channelid)
254                 {
255 //                      eDebug("found shared channel..");
256                         channel = i->m_channel;
257                         return 0;
258                 }
259         }
260         
261                 /* no currently available channel is tuned to this channelid. create a new one, if possible. */
262                 
263                 /* allocate a frontend. */
264         
265         ePtr<eDVBAllocatedFrontend> fe;
266         
267         if (allocateFrontend(channelid, fe))
268                 return errNoFrontend;
269         
270         ePtr<eDVBAllocatedDemux> demux;
271         
272         if (allocateDemux(*fe, demux))
273                 return errNoDemux;
274         
275         RESULT res;
276         eDVBChannel *ch;
277         ch = new eDVBChannel(this, fe, demux);
278
279         ePtr<iDVBFrontend> myfe;
280         if (!ch->getFrontend(myfe))
281                 myfe->setSEC(m_sec);
282
283         res = ch->setChannel(channelid);
284         if (res)
285         {
286                 channel = 0;
287                 return errChidNotFound;
288         }
289         
290         channel = ch;
291         return 0;
292 }
293
294 RESULT eDVBResourceManager::allocateRawChannel(eUsePtr<iDVBChannel> &channel)
295 {
296         ePtr<eDVBAllocatedFrontend> fe;
297         
298         if (allocateFrontend(eDVBChannelID(), fe))
299                 return errNoFrontend;
300         
301         ePtr<eDVBAllocatedDemux> demux;
302         
303         if (allocateDemux(*fe, demux))
304                 return errNoDemux;
305         
306         eDVBChannel *ch;
307         ch = new eDVBChannel(this, fe, demux);
308
309         ePtr<iDVBFrontend> myfe;
310         if (!ch->getFrontend(myfe))
311                 myfe->setSEC(m_sec);
312
313         channel = ch;
314         return 0;
315 }
316
317
318 RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr<iDVBPVRChannel> &channel)
319 {
320         ePtr<eDVBAllocatedDemux> demux;
321         
322         if (allocateDemux(0, demux))
323                 return errNoDemux;
324         
325         eDVBChannel *ch;
326         ch = new eDVBChannel(this, 0, demux);
327         
328         channel = ch;
329         return 0;
330 }
331
332 RESULT eDVBResourceManager::addChannel(const eDVBChannelID &chid, eDVBChannel *ch)
333 {
334         eDebug("add channel %p", ch);
335         m_active_channels.push_back(active_channel(chid, ch));
336         /* emit */ m_channelAdded(ch);
337         return 0;
338 }
339
340 RESULT eDVBResourceManager::removeChannel(eDVBChannel *ch)
341 {
342         int cnt = 0;
343         for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end();)
344         {
345                 if (i->m_channel == ch)
346                 {
347                         i = m_active_channels.erase(i);
348                         ++cnt;
349                 } else
350                         ++i;
351         }
352         ASSERT(cnt == 1);
353         if (cnt == 1)
354                 return 0;
355         return -ENOENT;
356 }
357
358 RESULT eDVBResourceManager::connectChannelAdded(const Slot1<void,eDVBChannel*> &channelAdded, ePtr<eConnection> &connection)
359 {
360         connection = new eConnection((eDVBResourceManager*)this, m_channelAdded.connect(channelAdded));
361         return 0;
362 }
363
364 DEFINE_REF(eDVBChannel);
365
366 eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, eDVBAllocatedFrontend *frontend, eDVBAllocatedDemux *demux): m_state(state_idle), m_mgr(mgr)
367 {
368         m_frontend = frontend;
369         m_demux = demux;
370
371         m_pvr_thread = 0;
372         
373         if (m_frontend)
374                 m_frontend->get().connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged);
375 }
376
377 eDVBChannel::~eDVBChannel()
378 {
379         if (m_channel_id)
380                 m_mgr->removeChannel(this);
381         
382         if (m_pvr_thread)
383         {
384                 m_pvr_thread->stop();
385                 ::close(m_pvr_fd_src);
386                 ::close(m_pvr_fd_dst);
387                 delete m_pvr_thread;
388         }
389 }
390
391 void eDVBChannel::frontendStateChanged(iDVBFrontend*fe)
392 {
393         eDebug("fe state changed!");
394         int state, ourstate = 0;
395         
396                 /* if we are already in shutdown, don't change state. */
397         if (m_state == state_release)
398                 return;
399         
400         if (fe->getState(state))
401                 return;
402         
403         if (state == iDVBFrontend::stateLock)
404         {
405                 eDebug("OURSTATE: ok");
406                 ourstate = state_ok;
407         } else if (state == iDVBFrontend::stateTuning)
408         {
409                 eDebug("OURSTATE: tuning");
410                 ourstate = state_tuning;
411         } else if (state == iDVBFrontend::stateFailed)
412         {
413                 eDebug("OURSTATE: failed/unavailable");
414                 ourstate = state_unavailable;
415         } else
416                 eFatal("state unknown");
417         
418         if (ourstate != m_state)
419         {
420                 m_state = ourstate;
421                 m_stateChanged(this);
422         }
423 }
424
425 void eDVBChannel::AddUse()
426 {
427         ++m_use_count;
428 }
429
430 void eDVBChannel::ReleaseUse()
431 {
432         if (!--m_use_count)
433         {
434                 m_state = state_release;
435                 m_stateChanged(this);
436         }
437 }
438
439 RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid)
440 {
441         if (m_channel_id)
442                 m_mgr->removeChannel(this);
443                 
444         if (!channelid)
445                 return 0;
446
447         ePtr<iDVBChannelList> list;
448         
449         if (m_mgr->getChannelList(list))
450         {
451                 eDebug("no channel list set!");
452                 return -ENOENT;
453         }
454         
455         eDebug("tuning to chid: ns: %08x tsid %04x onid %04x",
456                 channelid.dvbnamespace.get(), channelid.transport_stream_id.get(), channelid.original_network_id.get());
457
458         ePtr<iDVBFrontendParameters> feparm;
459         if (list->getChannelFrontendData(channelid, feparm))
460         {
461                 eDebug("channel not found!");
462                 return -ENOENT;
463         }
464         eDebug("allocateChannel: channel found..");
465         
466         if (!m_frontend)
467         {
468                 eDebug("no frontend to tune!");
469                 return -ENODEV;
470         }
471         
472         m_channel_id = channelid;
473         m_mgr->addChannel(channelid, this);
474         m_state = state_tuning;
475         return m_frontend->get().tune(*feparm);
476 }
477
478 RESULT eDVBChannel::connectStateChange(const Slot1<void,iDVBChannel*> &stateChange, ePtr<eConnection> &connection)
479 {
480         connection = new eConnection((iDVBChannel*)this, m_stateChanged.connect(stateChange));
481         return 0;
482 }
483
484 RESULT eDVBChannel::getState(int &state)
485 {
486         state = m_state;
487         return 0;
488 }
489
490 RESULT eDVBChannel::setCIRouting(const eDVBCIRouting &routing)
491 {
492         return -1;
493 }
494
495 RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux)
496 {
497         demux = &m_demux->get();
498         return 0;
499 }
500
501 RESULT eDVBChannel::getFrontend(ePtr<iDVBFrontend> &frontend)
502 {
503         frontend = &m_frontend->get();
504         if (frontend)
505                 return 0;
506         else
507                 return -ENODEV;
508 }
509
510 RESULT eDVBChannel::playFile(const char *file)
511 {
512         ASSERT(!m_frontend);
513         if (m_pvr_thread)
514         {
515                 m_pvr_thread->stop();
516                 delete m_pvr_thread;
517                 m_pvr_thread = 0;
518         }
519         
520         m_tstools.openFile(file);
521         
522                 /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST,
523                    THEN DO A REAL FIX HERE! */
524         
525                 /* (this codepath needs to be improved anyway.) */
526         m_pvr_fd_dst = open("/dev/misc/pvr", O_WRONLY);
527         if (m_pvr_fd_dst < 0)
528         {
529                 eDebug("can't open /dev/misc/pvr - you need to buy the new(!) $$$ box! (%m)");
530                 return -ENODEV;
531         }
532         
533         m_pvr_fd_src = open(file, O_RDONLY);
534         if (m_pvr_fd_src < 0)
535         {
536                 eDebug("can't open PVR m_pvr_fd_src file %s (%m)", file);
537                 close(m_pvr_fd_dst);
538                 return -ENOENT;
539         }
540         
541         m_state = state_ok;
542         m_stateChanged(this);
543         
544         m_pvr_thread = new eFilePushThread();
545         m_pvr_thread->start(m_pvr_fd_src, m_pvr_fd_dst);
546 }
547
548 RESULT eDVBChannel::getLength(pts_t &len)
549 {
550         return m_tstools.calcLen(len);
551 }
552
553 RESULT eDVBChannel::getCurrentPosition(pts_t &pos)
554 {
555         off_t begin = 0;
556                 /* getPTS for offset 0 is cached, so it doesn't harm. */
557         int r = m_tstools.getPTS(begin, pos);
558         if (r)
559         {
560                 eDebug("tstools getpts(0) failed!");
561                 return r;
562         }
563         
564         pts_t now;
565         
566         r = m_demux->get().getSTC(now);
567
568         if (r)
569         {
570                 eDebug("demux getSTC failed");
571                 return -1;
572         }
573         
574         eDebug("STC: %08llx PTS: %08llx, diff %lld", now, pos, now - pos);
575         
576                 /* when we are less than 10 seconds before the start, return 0. */
577                 /* (we're just waiting for the timespam to start) */
578         if ((now < pos) && ((pos - now) < 90000 * 10))
579         {
580                 pos = 0;
581                 return 0;
582         }
583         
584         if (now < pos) /* wrap around */
585                 pos = now + ((pts_t)1)<<33 - pos;
586         else
587                 pos = now - pos;
588         
589         return 0;
590 }