- add eUsePtr for eDVBChannels
[enigma2.git] / lib / dvb / dvb.cpp
1 #include <lib/dvb/idvb.h>
2 #include <lib/base/eerror.h>
3 #include <lib/dvb/dvb.h>
4 #include <lib/dvb/sec.h>
5 #include <errno.h>
6
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10
11 DEFINE_REF(eDVBRegisteredFrontend);
12 DEFINE_REF(eDVBRegisteredDemux);
13
14 DEFINE_REF(eDVBAllocatedFrontend);
15
16 eDVBAllocatedFrontend::eDVBAllocatedFrontend(eDVBRegisteredFrontend *fe): m_fe(fe)
17 {
18         m_fe->m_inuse++;
19 }
20
21 eDVBAllocatedFrontend::~eDVBAllocatedFrontend()
22 {
23         --m_fe->m_inuse;
24 }
25
26 DEFINE_REF(eDVBAllocatedDemux);
27
28 eDVBAllocatedDemux::eDVBAllocatedDemux(eDVBRegisteredDemux *demux): m_demux(demux)
29 {
30         m_demux->m_inuse++;
31 }
32
33 eDVBAllocatedDemux::~eDVBAllocatedDemux()
34 {
35         --m_demux->m_inuse;
36 }
37
38 DEFINE_REF(eDVBResourceManager);
39
40 eDVBResourceManager *eDVBResourceManager::instance;
41
42 eDVBResourceManager::eDVBResourceManager()
43 {
44         avail = 1;
45         busy = 0;
46         m_sec = new eDVBSatelliteEquipmentControl;
47         if (!instance)
48                 instance = this;
49                 
50                 /* search available adapters... */
51
52                 // add linux devices
53         
54         int num_adapter = 0;
55         while (eDVBAdapterLinux::exist(num_adapter))
56         {
57                 addAdapter(new eDVBAdapterLinux(num_adapter));
58                 num_adapter++;
59         }
60         
61         eDebug("found %d adapter, %d frontends and %d demux", 
62                 m_adapter.size(), m_frontend.size(), m_demux.size());
63 }
64
65
66 DEFINE_REF(eDVBAdapterLinux);
67 eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr)
68 {
69                 // scan frontends
70         int num_fe = 0;
71         
72         eDebug("scanning for frontends..");
73         while (1)
74         {
75                 struct stat s;
76                 char filename[128];
77 #if HAVE_DVB_API_VERSION < 3
78                 sprintf(filename, "/dev/dvb/card%d/frontend%d", m_nr, num_fe);
79 #else
80                 sprintf(filename, "/dev/dvb/adapter%d/frontend%d", m_nr, num_fe);
81 #endif
82                 if (stat(filename, &s))
83                         break;
84                 ePtr<eDVBFrontend> fe;
85
86                 int ok = 0;
87                 fe = new eDVBFrontend(m_nr, num_fe, ok);
88                 if (ok)
89                         m_frontend.push_back(fe);
90                 ++num_fe;
91         }
92         
93                 // scan demux
94         int num_demux = 0;
95         while (1)
96         {
97                 struct stat s;
98                 char filename[128];
99 #if HAVE_DVB_API_VERSION < 3
100                 sprintf(filename, "/dev/dvb/card%d/demux%d", m_nr, num_demux);
101 #else
102                 sprintf(filename, "/dev/dvb/adapter%d/demux%d", m_nr, num_demux);
103 #endif
104                 if (stat(filename, &s))
105                         break;
106                 ePtr<eDVBDemux> demux;
107                 
108                 demux = new eDVBDemux(m_nr, num_demux);
109                 m_demux.push_back(demux);
110                         
111                 ++num_demux;
112         }
113 }
114
115 int eDVBAdapterLinux::getNumDemux()
116 {
117         return m_demux.size();
118 }
119
120 RESULT eDVBAdapterLinux::getDemux(ePtr<eDVBDemux> &demux, int nr)
121 {
122         eSmartPtrList<eDVBDemux>::iterator i(m_demux.begin());
123         while (nr && (i != m_demux.end()))
124         {
125                 --nr;
126                 ++i;
127         }
128         
129         if (i != m_demux.end())
130                 demux = *i;
131         else
132                 return -1;
133                 
134         return 0;
135 }
136
137 int eDVBAdapterLinux::getNumFrontends()
138 {
139         return m_frontend.size();
140 }
141
142 RESULT eDVBAdapterLinux::getFrontend(ePtr<eDVBFrontend> &fe, int nr)
143 {
144         eSmartPtrList<eDVBFrontend>::iterator i(m_frontend.begin());
145         while (nr && (i != m_frontend.end()))
146         {
147                 --nr;
148                 ++i;
149         }
150         
151         if (i != m_frontend.end())
152                 fe = *i;
153         else
154                 return -1;
155                 
156         return 0;
157 }
158
159 int eDVBAdapterLinux::exist(int nr)
160 {
161         struct stat s;
162         char filename[128];
163 #if HAVE_DVB_API_VERSION < 3
164         sprintf(filename, "/dev/dvb/card%d", nr);
165 #else
166         sprintf(filename, "/dev/dvb/adapter%d", nr);
167 #endif
168         if (!stat(filename, &s))
169                 return 1;
170         return 0;
171 }
172
173 eDVBResourceManager::~eDVBResourceManager()
174 {
175         if (instance == this)
176                 instance = 0;
177
178 }
179
180 void eDVBResourceManager::addAdapter(iDVBAdapter *adapter)
181 {
182         int num_fe = adapter->getNumFrontends();
183         int num_demux = adapter->getNumDemux();
184         
185         m_adapter.push_back(adapter);
186         
187         int i;
188         for (i=0; i<num_demux; ++i)
189         {
190                 ePtr<eDVBDemux> demux;
191                 if (!adapter->getDemux(demux, i))
192                         m_demux.push_back(new eDVBRegisteredDemux(demux, adapter));
193         }
194
195         for (i=0; i<num_fe; ++i)
196         {
197                 ePtr<eDVBFrontend> frontend;
198                 if (!adapter->getFrontend(frontend, i))
199                         m_frontend.push_back(new eDVBRegisteredFrontend(frontend, adapter));
200         }
201 }
202
203 RESULT eDVBResourceManager::allocateFrontend(const eDVBChannelID &chid, ePtr<eDVBAllocatedFrontend> &fe)
204 {
205                 /* find first unused frontend. we ignore compatibility for now. */
206         for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
207                 if (!i->m_inuse)
208                 {
209                         fe = new eDVBAllocatedFrontend(i);
210                         return 0;
211                 }
212         return -1;
213 }
214
215 RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBAllocatedDemux> &demux)
216 {
217                 /* find first unused demux which is on same adapter as frontend */
218         for (eSmartPtrList<eDVBRegisteredDemux>::iterator i(m_demux.begin()); i != m_demux.end(); ++i)
219                 if ((!i->m_inuse) && (i->m_adapter == fe->m_adapter))
220                 {
221                         demux = new eDVBAllocatedDemux(i);
222                         return 0;
223                 }
224         return -1;
225 }
226
227 RESULT eDVBResourceManager::setChannelList(iDVBChannelList *list)
228 {
229         m_list = list;
230         return 0;
231 }
232
233 RESULT eDVBResourceManager::getChannelList(ePtr<iDVBChannelList> &list)
234 {
235         list = m_list;
236         if (list)
237                 return 0;
238         else
239                 return -ENOENT;
240 }
241
242
243 RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUsePtr<iDVBChannel> &channel)
244 {
245                 /* first, check if a channel is already existing. */
246         
247 //      eDebug("allocate channel.. %04x:%04x", channelid.transport_stream_id.get(), channelid.original_network_id.get());
248         for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end(); ++i)
249         {
250 //              eDebug("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
251                 if (i->m_channel_id == channelid)
252                 {
253 //                      eDebug("found shared channel..");
254                         channel = i->m_channel;
255                         return 0;
256                 }
257         }
258         
259                 /* no currently available channel is tuned to this channelid. create a new one, if possible. */
260                 
261                 /* allocate a frontend. */
262         
263         ePtr<eDVBAllocatedFrontend> fe;
264         
265         if (allocateFrontend(channelid, fe))
266                 return errNoFrontend;
267         
268         ePtr<eDVBAllocatedDemux> demux;
269         
270         if (allocateDemux(*fe, demux))
271                 return errNoDemux;
272         
273         RESULT res;
274         eDVBChannel *ch;
275         ch = new eDVBChannel(this, fe, demux);
276
277         ePtr<iDVBFrontend> myfe;
278         if (!ch->getFrontend(myfe))
279                 myfe->setSEC(m_sec);
280
281         res = ch->setChannel(channelid);
282         if (res)
283         {
284                 channel = 0;
285                 return errChidNotFound;
286         }
287         
288         channel = ch;
289         return 0;
290 }
291
292 RESULT eDVBResourceManager::allocateRawChannel(eUsePtr<iDVBChannel> &channel)
293 {
294         ePtr<eDVBAllocatedFrontend> fe;
295         
296         if (allocateFrontend(eDVBChannelID(), fe))
297                 return errNoFrontend;
298         
299         ePtr<eDVBAllocatedDemux> demux;
300         
301         if (allocateDemux(*fe, demux))
302                 return errNoDemux;
303         
304         eDVBChannel *ch;
305         ch = new eDVBChannel(this, fe, demux);
306
307         ePtr<iDVBFrontend> myfe;
308         if (!ch->getFrontend(myfe))
309                 myfe->setSEC(m_sec);
310
311         channel = ch;
312         return 0;
313 }
314
315 RESULT eDVBResourceManager::allocatePVRChannel(int caps)
316 {
317         return -1; // will nicht, mag nicht, und das interface ist auch kaputt
318 }
319
320 RESULT eDVBResourceManager::addChannel(const eDVBChannelID &chid, eDVBChannel *ch)
321 {
322         eDebug("add channel %p", ch);
323         m_active_channels.push_back(active_channel(chid, ch));
324         /* emit */ m_channelAdded(ch);
325         return 0;
326 }
327
328 RESULT eDVBResourceManager::removeChannel(eDVBChannel *ch)
329 {
330         int cnt = 0;
331         for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end();)
332         {
333                 if (i->m_channel == ch)
334                 {
335                         i = m_active_channels.erase(i);
336                         ++cnt;
337                         /* emit */ m_channelRemoved(ch);
338                 } else
339                         ++i;
340         }
341         ASSERT(cnt == 1);
342         if (cnt == 1)
343                 return 0;
344         return -ENOENT;
345 }
346
347 RESULT eDVBResourceManager::connectChannelAdded(const Slot1<void,eDVBChannel*> &channelAdded, ePtr<eConnection> &connection)
348 {
349         connection = new eConnection((eDVBResourceManager*)this, m_channelAdded.connect(channelAdded));
350         return 0;
351 }
352
353 RESULT eDVBResourceManager::connectChannelRemoved(const Slot1<void,eDVBChannel*> &channelRemoved, ePtr<eConnection> &connection)
354 {
355         connection = new eConnection((eDVBResourceManager*)this, m_channelRemoved.connect(channelRemoved));
356         return 0;
357 }
358
359 RESULT eDVBResourceManager::connectChannelRunning(const Slot1<void,iDVBChannel*> &channelRunning, ePtr<eConnection> &connection)
360 {
361         connection = new eConnection((eDVBResourceManager*)this, m_channelRunning.connect(channelRunning));
362         return 0;
363 }
364
365 DEFINE_REF(eDVBChannel);
366
367 eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, eDVBAllocatedFrontend *frontend, eDVBAllocatedDemux *demux): m_state(state_idle), m_mgr(mgr)
368 {
369         m_frontend = frontend;
370         m_demux = demux;
371         
372         if (m_frontend)
373                 m_frontend->get().connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged);
374 }
375
376 eDVBChannel::~eDVBChannel()
377 {
378         if (m_channel_id)
379                 m_mgr->removeChannel(this);
380 }
381
382 void eDVBChannel::frontendStateChanged(iDVBFrontend*fe)
383 {
384         eDebug("fe state changed!");
385         int state, ourstate = 0;
386         
387                 /* if we are already in shutdown, don't change state. */
388         if (m_state == state_release)
389                 return;
390         
391         if (fe->getState(state))
392                 return;
393         
394         if (state == iDVBFrontend::stateLock)
395         {
396                 eDebug("OURSTATE: ok");
397                 ourstate = state_ok;
398         } else if (state == iDVBFrontend::stateTuning)
399         {
400                 eDebug("OURSTATE: tuning");
401                 ourstate = state_tuning;
402         } else if (state == iDVBFrontend::stateFailed)
403         {
404                 eDebug("OURSTATE: failed/unavailable");
405                 ourstate = state_unavailable;
406         } else
407                 eFatal("state unknown");
408         
409         if (ourstate != m_state)
410         {
411                 m_state = ourstate;
412                 m_stateChanged(this);
413         }
414 }
415
416 void eDVBChannel::AddUse()
417 {
418         ++m_use_count;
419 }
420
421 void eDVBChannel::ReleaseUse()
422 {
423         if (!--m_use_count)
424         {
425                 m_state = state_release;
426                 m_stateChanged(this);
427         }
428 }
429
430 RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid)
431 {
432         if (m_channel_id)
433                 m_mgr->removeChannel(this);
434                 
435         if (!channelid)
436                 return 0;
437
438         ePtr<iDVBChannelList> list;
439         
440         if (m_mgr->getChannelList(list))
441         {
442                 eDebug("no channel list set!");
443                 return -ENOENT;
444         }
445         
446         eDebug("tuning to chid: ns: %08x tsid %04x onid %04x",
447                 channelid.dvbnamespace.get(), channelid.transport_stream_id.get(), channelid.original_network_id.get());
448
449         ePtr<iDVBFrontendParameters> feparm;
450         if (list->getChannelFrontendData(channelid, feparm))
451         {
452                 eDebug("channel not found!");
453                 return -ENOENT;
454         }
455         eDebug("allocateChannel: channel found..");
456         
457         if (!m_frontend)
458         {
459                 eDebug("no frontend to tune!");
460                 return -ENODEV;
461         }
462         
463         m_channel_id = channelid;
464         m_mgr->addChannel(channelid, this);
465         m_state = state_tuning;
466         return m_frontend->get().tune(*feparm);
467 }
468
469 RESULT eDVBChannel::connectStateChange(const Slot1<void,iDVBChannel*> &stateChange, ePtr<eConnection> &connection)
470 {
471         connection = new eConnection((iDVBChannel*)this, m_stateChanged.connect(stateChange));
472         return 0;
473 }
474
475 RESULT eDVBChannel::getState(int &state)
476 {
477         state = m_state;
478         return 0;
479 }
480
481 RESULT eDVBChannel::setCIRouting(const eDVBCIRouting &routing)
482 {
483         return -1;
484 }
485
486 RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux)
487 {
488         demux = &m_demux->get();
489         return 0;
490 }
491
492 RESULT eDVBChannel::getFrontend(ePtr<iDVBFrontend> &frontend)
493 {
494         frontend = &m_frontend->get();
495         if (frontend)
496                 return 0;
497         else
498                 return -ENODEV;
499 }