- add prelim. pvr playback.
[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                 delete m_pvr_thread;
386         }
387 }
388
389 void eDVBChannel::frontendStateChanged(iDVBFrontend*fe)
390 {
391         eDebug("fe state changed!");
392         int state, ourstate = 0;
393         
394                 /* if we are already in shutdown, don't change state. */
395         if (m_state == state_release)
396                 return;
397         
398         if (fe->getState(state))
399                 return;
400         
401         if (state == iDVBFrontend::stateLock)
402         {
403                 eDebug("OURSTATE: ok");
404                 ourstate = state_ok;
405         } else if (state == iDVBFrontend::stateTuning)
406         {
407                 eDebug("OURSTATE: tuning");
408                 ourstate = state_tuning;
409         } else if (state == iDVBFrontend::stateFailed)
410         {
411                 eDebug("OURSTATE: failed/unavailable");
412                 ourstate = state_unavailable;
413         } else
414                 eFatal("state unknown");
415         
416         if (ourstate != m_state)
417         {
418                 m_state = ourstate;
419                 m_stateChanged(this);
420         }
421 }
422
423 void eDVBChannel::AddUse()
424 {
425         ++m_use_count;
426 }
427
428 void eDVBChannel::ReleaseUse()
429 {
430         if (!--m_use_count)
431         {
432                 m_state = state_release;
433                 m_stateChanged(this);
434         }
435 }
436
437 RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid)
438 {
439         if (m_channel_id)
440                 m_mgr->removeChannel(this);
441                 
442         if (!channelid)
443                 return 0;
444
445         ePtr<iDVBChannelList> list;
446         
447         if (m_mgr->getChannelList(list))
448         {
449                 eDebug("no channel list set!");
450                 return -ENOENT;
451         }
452         
453         eDebug("tuning to chid: ns: %08x tsid %04x onid %04x",
454                 channelid.dvbnamespace.get(), channelid.transport_stream_id.get(), channelid.original_network_id.get());
455
456         ePtr<iDVBFrontendParameters> feparm;
457         if (list->getChannelFrontendData(channelid, feparm))
458         {
459                 eDebug("channel not found!");
460                 return -ENOENT;
461         }
462         eDebug("allocateChannel: channel found..");
463         
464         if (!m_frontend)
465         {
466                 eDebug("no frontend to tune!");
467                 return -ENODEV;
468         }
469         
470         m_channel_id = channelid;
471         m_mgr->addChannel(channelid, this);
472         m_state = state_tuning;
473         return m_frontend->get().tune(*feparm);
474 }
475
476 RESULT eDVBChannel::connectStateChange(const Slot1<void,iDVBChannel*> &stateChange, ePtr<eConnection> &connection)
477 {
478         connection = new eConnection((iDVBChannel*)this, m_stateChanged.connect(stateChange));
479         return 0;
480 }
481
482 RESULT eDVBChannel::getState(int &state)
483 {
484         state = m_state;
485         return 0;
486 }
487
488 RESULT eDVBChannel::setCIRouting(const eDVBCIRouting &routing)
489 {
490         return -1;
491 }
492
493 RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux)
494 {
495         demux = &m_demux->get();
496         return 0;
497 }
498
499 RESULT eDVBChannel::getFrontend(ePtr<iDVBFrontend> &frontend)
500 {
501         frontend = &m_frontend->get();
502         if (frontend)
503                 return 0;
504         else
505                 return -ENODEV;
506 }
507
508 RESULT eDVBChannel::playFile(const char *file)
509 {
510         ASSERT(!m_frontend);
511         if (m_pvr_thread)
512         {
513                 m_pvr_thread->stop();
514                 delete m_pvr_thread;
515                 m_pvr_thread = 0;
516         }
517         
518                 /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST,
519                    THEN DO A REAL FIX HERE! */
520         
521         
522                 /* (this codepath needs to be improved anyway.) */
523         int dest = open("/dev/misc/pvr", O_WRONLY);
524         if (dest < 0)
525         {
526                 eDebug("can't open /dev/misc/pvr - you need to buy the new(!) $$$ box! (%m)");
527                 return -ENODEV;
528         }
529         
530         int source = open(file, O_RDONLY);
531         if (source < 0)
532         {
533                 eDebug("can't open PVR source file %s (%m)", file);
534                 close(dest);
535                 return -ENOENT;
536         }
537
538         m_state = state_ok;
539         m_stateChanged(this);
540         
541         m_pvr_thread = new eFilePushThread();
542         m_pvr_thread->start(source, dest);
543 }