summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--HISTORY53
-rw-r--r--MANUAL16
-rw-r--r--Makefile4
-rw-r--r--PLUGINS.html47
-rw-r--r--channels.h3
-rw-r--r--ci.c1742
-rw-r--r--ci.h247
-rw-r--r--config.h10
-rw-r--r--device.c276
-rw-r--r--device.h51
-rw-r--r--dvbci.c108
-rw-r--r--dvbci.h31
-rw-r--r--dvbdevice.c77
-rw-r--r--dvbdevice.h10
-rw-r--r--i18n.c200
-rw-r--r--menu.c416
-rw-r--r--menu.h28
-rw-r--r--osdbase.c23
-rw-r--r--osdbase.h11
-rw-r--r--pat.c14
-rw-r--r--pat.h4
-rw-r--r--receiver.c6
-rw-r--r--receiver.h19
-rw-r--r--recorder.c10
-rw-r--r--recorder.h8
-rw-r--r--thread.c30
-rw-r--r--thread.h3
-rw-r--r--tools.c6
-rw-r--r--tools.h6
-rw-r--r--transfer.c17
-rw-r--r--transfer.h6
-rw-r--r--vdr.c17
32 files changed, 2079 insertions, 1420 deletions
diff --git a/HISTORY b/HISTORY
index e290b648..4649a223 100644
--- a/HISTORY
+++ b/HISTORY
@@ -5028,3 +5028,56 @@ Video Disk Recorder Revision History
2007-01-07: Version 1.4.5
- Official release.
+
+2007-01-07: Version 1.5.0
+
+- The CAM handling has been refactored. Instead of a cCiHandler per device there
+ is now an abstract cCiAdapter and a cCamSlot. This allows each slot to be
+ accessed individually.
+- The general 15 seconds workaround time before opening the CAM menu has been
+ removed. If the CAM menu doesn't open within a timeout, the enter menu command
+ is now sent again.
+- If a CAM is reset or pulled and reinserted, it now automatically starts
+ decrypting the current channel again.
+- The Setup/CAM menu now dynamically refreshes its items and displays whether
+ a CAM is present or ready. The 'Reset' function no longer leaves the menu.
+- The CAM menu will now be openend when pressing the Ok key on a slot entry.
+- The CAM menu now stays within the current menu context and doesn't close and
+ reopen the menu every time an option is selected.
+- When an encrypted channel is switched to for the first time, VDR now checks
+ explicitly whether a CAM can actually decrypt that channel. If there is more
+ than one CAM in the system that claims to be able to decrypt the channel,
+ they are all tried in turn.
+ To make this possible, an encrypted channel needs to be received in Transfer
+ Mode when it is switched to for the first time, so that VDR can determine
+ whether the TS packets are actually decrypted. Once a channel is known to
+ be decrypted by a particular CAM, the next time it is switched to it will
+ be shown in normal live viewing mode.
+- A cDevice now automatically detaches all cReceiver objects that receive PIDs
+ that can't be decrypted with the current CAM. A plugin that attaches a cReceiver
+ to a device should therefore watch the receiver's IsAttached() function to
+ see if it is still attached to the device.
+- The cReceiver constructor no longer takes an 'int Ca' as its first parameter,
+ but rather a 'tChannelID ChannelID'. This is necessary for the device to be
+ able to determine which CAM a particular channel can be decrypted with. If the
+ channel is known to be unencrypted, or a plugin doesn't want to provide the
+ channel id for other reasons, an invalid tChannelID() can be given.
+- The cThread::Start() function now waits until a previous incarnation of this
+ thread has actually stopped. Before this it could happen that a thread's
+ Cancel(-1) function was called and immediately after that it was started again,
+ but the Start() function still found it to be 'active'.
+- The parameter NeedsDetachReceivers in cDevice::GetDevice(const cChannel *Channel, ...)
+ has been removed. A call to this function will automatically detach all receivers
+ from the device if it returns a non-NULL pointer.
+- The cTimeMs class now accepts an initial timeout value in its constructor.
+- A CAM is now explicitly instructed to stop decrypting when switching away from
+ an encrypted channel.
+- If the CAM in use can decrypt several channels at the same time, VDR can
+ now make use if this capability. Whether or not a CAM can decrypt more
+ than one channel is determined by sending it an initial empty QUERY command
+ and testing whether it replies to it.
+- Ca values in the range 0...F in channels.conf can still be used to assign a channel
+ to a particular device, but this will no longer work with encrypted channels because
+ without valid CA ids VDR can't decide which CAM slot to use. However, since VDR now
+ automatically determines which CAM can decrypt which channel, setting fixed
+ channel/device relations should no longer be necessary.
diff --git a/MANUAL b/MANUAL
index 52c2a94e..3377fb0d 100644
--- a/MANUAL
+++ b/MANUAL
@@ -667,13 +667,15 @@ Version 1.4
Use DiSEqC = no Generally turns DiSEqC support on or off.
- CICAM:
-
- CICAM DVBn m Shows the CAMs that each device contains, where 'n' is
- the number of the device, and 'm' is the number of the
- Common Interface slot of that device. The "Red" key
- can be pressed to enter the CAM menu, and the "Green" key
- triggers a reset of the selected CAM.
+ CAM:
+
+ n CAM Name Shows the CAM slots that are present in this system, where
+ 'n' is the number of the slot, followed by the name of the
+ CAM. If a CAM slot is empty, '-' is displayed as name, and
+ if it is in the process of being reset, its current status
+ is displayed. The "Red" key can be pressed to enter the CAM
+ menu, and the "Green" key triggers a reset of the selected
+ slot. The "Ok" key also opens the CAM menu.
Recording:
diff --git a/Makefile b/Makefile
index 1d73b79d..d70973a0 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
-# $Id: Makefile 1.95 2006/08/20 10:44:22 kls Exp $
+# $Id: Makefile 1.96 2007/01/07 14:38:00 kls Exp $
.DELETE_ON_ERROR:
@@ -32,7 +32,7 @@ DOXYFILE = Doxyfile
SILIB = $(LSIDIR)/libsi.a
-OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbosd.o\
+OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbci.o dvbosd.o\
dvbplayer.o dvbspu.o eit.o eitscan.o epg.o filter.o font.o i18n.o interface.o keys.o\
lirc.o menu.o menuitems.o nit.o osdbase.o osd.o pat.o player.o plugin.o rcu.o\
receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o\
diff --git a/PLUGINS.html b/PLUGINS.html
index 51424ce3..71c1b77e 100644
--- a/PLUGINS.html
+++ b/PLUGINS.html
@@ -6,7 +6,7 @@
<center><h1>The VDR Plugin System</h1></center>
-<center><b>Version 1.4.1</b></center>
+<center><b>Version 1.5.0</b></center>
<p>
<center>
Copyright &copy; 2006 Klaus Schmidinger<br>
@@ -14,6 +14,10 @@ Copyright &copy; 2006 Klaus Schmidinger<br>
<a href="http://www.cadsoft.de/vdr">www.cadsoft.de/vdr</a>
</center>
<p>
+<!--X1.5.0--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+Important modifications introduced in version 1.5.0 are marked like this.
+<!--X1.5.0--></td></tr></table>
+<p>
VDR provides an easy to use plugin interface that allows additional functionality
to be added to the program by implementing a dynamically loadable library file.
This interface allows programmers to develop additional functionality for VDR completely
@@ -72,6 +76,9 @@ structures and allows it to hook itself into specific areas to perform special a
<li><a href="#Devices">Devices</a>
<li><a href="#Audio">Audio</a>
<li><a href="#Remote Control">Remote Control</a>
+<!--X1.5.0--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+<li><a href="#Conditional Access">Conditional Access</a>
+<!--X1.5.0--></td></tr></table>
</ul>
</ul>
@@ -1727,6 +1734,7 @@ selecting which channel it shall tune to:
<p><table><tr><td bgcolor=#F0F0F0><pre>
virtual bool ProvidesSource(int Source) const;
+virtual bool ProvidesTransponder(const cChannel *Channel) const;
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
</pre></td></tr></table><p>
@@ -2038,5 +2046,42 @@ Put(uint64 Code, bool Repeat = false, bool Release = false);
The other parameters have the same meaning as in the first version of this function.
+<!--X1.5.0--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+<a name="Conditional Access"><hr><h2>Conditional Access</h2>
+
+<center><i><b>Members only!</b></i></center><p>
+
+Pay TV providers usually encrypt their broadcasts, so that only viewers who
+have the proper smart card can watch them. Such a smart card needs to be inserted
+into a CAM (Conditional Access Module), which in turn goes into a CI (Common
+Interface) slot.
+<p>
+VDR's mechanisms for supporting Conditional Access are mainly the two classes
+<tt>cCiAdapter</tt> and <tt>cCamSlot</tt>. A <tt>cCiAdapter</tt> handles exactly
+one CI, and can provide several CAM slots, represented by the appropriate
+number of <tt>cCamSlot</tt> objects.
+<p>
+In order to decrypt a particular channel, a <tt>cCiAdapter</tt> with a <tt>cCamSlot</tt>
+that contains the necessary CAM will be assigned to a <tt>cDevice</tt>, and exactly
+one of its CAM slots will be activated. Whether or not a <tt>cCiAdapter</tt> can
+be assigned to a particular device depends on the hardware implementation.
+Some devices (like the Siemens/Technotrend DVB cards) are hardwired with their
+CI adapters, so the <tt>cCiAdapter</tt> for these can only be used with one device.
+Other hardware implementations may allow CI adapters and devices to be connected
+through some sort of matrix switch. When trying to decrypt an encrypted channel,
+VDR will automatically select a useful combination of device and CAM slot.
+<p>
+If a plugin implements a derived <tt>cCiAdapter</tt>, it has to implement
+several low level functions that handle the actual data transfer (see <tt>dvbci.c</tt>
+for example). The decision whether the adapter can actually be assigned to different
+devices is made in the function
+
+<p><table><tr><td bgcolor=#F0F0F0><pre>
+virtual bool Assign(cDevice *Device, bool Query = false);
+</pre></td></tr></table><p>
+
+See the description of this function in <tt>ci.h</tt> for details.
+<!--X1.5.0--></td></tr></table>
+
</body>
</html>
diff --git a/channels.h b/channels.h
index c0dd37e0..e34cb0ef 100644
--- a/channels.h
+++ b/channels.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: channels.h 1.42 2006/05/28 15:03:56 kls Exp $
+ * $Id: channels.h 1.43 2007/01/07 14:37:35 kls Exp $
*/
#ifndef __CHANNELS_H
@@ -179,6 +179,7 @@ public:
const char *Dlang(int i) const { return (0 <= i && i < MAXDPIDS) ? dlangs[i] : ""; }
const char *Slang(int i) const { return (0 <= i && i < MAXSPIDS) ? slangs[i] : ""; }
int Tpid(void) const { return tpid; }
+ const int *Caids(void) const { return caids; }
int Ca(int Index = 0) const { return Index < MAXCAIDS ? caids[Index] : 0; }
int Nid(void) const { return nid; }
int Tid(void) const { return tid; }
diff --git a/ci.c b/ci.c
index cce7b5d0..a69f03bc 100644
--- a/ci.c
+++ b/ci.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: ci.c 1.46 2007/01/05 10:08:46 kls Exp $
+ * $Id: ci.c 1.47 2007/01/07 14:38:03 kls Exp $
*/
#include "ci.h"
@@ -17,38 +17,18 @@
#include <sys/ioctl.h>
#include <time.h>
#include <unistd.h>
+#include "device.h"
#include "pat.h"
#include "tools.h"
-/* these might come in handy in case you want to use this code without VDR's other files:
-#ifndef MALLOC
-#define MALLOC(type, size) (type *)malloc(sizeof(type) * (size))
-#endif
-
-#ifndef esyslog
-static int SysLogLevel = 3;
-#define esyslog(a...) void( (SysLogLevel > 0) ? void(fprintf(stderr, a)), void(fprintf(stderr, "\n")) : void() )
-#define isyslog(a...) void( (SysLogLevel > 1) ? void(fprintf(stderr, a)), void(fprintf(stderr, "\n")) : void() )
-#define dsyslog(a...) void( (SysLogLevel > 2) ? void(fprintf(stderr, a)), void(fprintf(stderr, "\n")) : void() )
-#endif
-*/
-
// Set these to 'true' for debug output:
static bool DumpTPDUDataTransfer = false;
static bool DebugProtocol = false;
+static bool DumpPolls = false;
+static bool DumpDateTime = false;
#define dbgprotocol(a...) if (DebugProtocol) fprintf(stderr, a)
-#define OK 0
-#define TIMEOUT -1
-#define ERROR -2
-
-// --- Workarounds -----------------------------------------------------------
-
-// The Irdeto AllCAM 4.7 (and maybe others, too) does not react on AOT_ENTER_MENU
-// during the first few seconds of a newly established connection
-#define WRKRND_TIME_BEFORE_ENTER_MENU 15 // seconds
-
// --- Helper functions ------------------------------------------------------
#define SIZE_INDICATOR 0x80
@@ -144,27 +124,29 @@ static char *GetString(int &Length, const uint8_t **Data)
class cTPDU {
private:
int size;
- uint8_t data[MAX_TPDU_SIZE];
+ uint8_t buffer[MAX_TPDU_SIZE];
const uint8_t *GetData(const uint8_t *Data, int &Length);
public:
cTPDU(void) { size = 0; }
cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length = 0, const uint8_t *Data = NULL);
- uint8_t Slot(void) { return data[0]; }
- uint8_t Tcid(void) { return data[1]; }
- uint8_t Tag(void) { return data[2]; }
- const uint8_t *Data(int &Length) { return GetData(data + 3, Length); }
+ uint8_t Slot(void) { return buffer[0]; }
+ uint8_t Tcid(void) { return buffer[1]; }
+ uint8_t Tag(void) { return buffer[2]; }
+ const uint8_t *Data(int &Length) { return GetData(buffer + 3, Length); }
uint8_t Status(void);
- int Write(int fd);
- int Read(int fd);
- void Dump(int fd, bool Outgoing);
+ uint8_t *Buffer(void) { return buffer; }
+ int Size(void) { return size; }
+ void SetSize(int Size) { size = Size; }
+ int MaxSize(void) { return sizeof(buffer); }
+ void Dump(int SlotNumber, bool Outgoing);
};
cTPDU::cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length, const uint8_t *Data)
{
size = 0;
- data[0] = Slot;
- data[1] = Tcid;
- data[2] = Tag;
+ buffer[0] = Slot;
+ buffer[1] = Tcid;
+ buffer[2] = Tag;
switch (Tag) {
case T_RCV:
case T_CREATE_TC:
@@ -172,72 +154,51 @@ cTPDU::cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length, const uint8_t
case T_DELETE_TC:
case T_DTC_REPLY:
case T_REQUEST_TC:
- data[3] = 1; // length
- data[4] = Tcid;
+ buffer[3] = 1; // length
+ buffer[4] = Tcid;
size = 5;
break;
case T_NEW_TC:
case T_TC_ERROR:
if (Length == 1) {
- data[3] = 2; // length
- data[4] = Tcid;
- data[5] = Data[0];
+ buffer[3] = 2; // length
+ buffer[4] = Tcid;
+ buffer[5] = Data[0];
size = 6;
}
else
- esyslog("ERROR: invalid data length for TPDU tag 0x%02X: %d", Tag, Length);
+ esyslog("ERROR: invalid data length for TPDU tag 0x%02X: %d (%d/%d)", Tag, Length, Slot, Tcid);
break;
case T_DATA_LAST:
case T_DATA_MORE:
if (Length <= MAX_TPDU_DATA) {
- uint8_t *p = data + 3;
+ uint8_t *p = buffer + 3;
p = SetLength(p, Length + 1);
*p++ = Tcid;
if (Length)
memcpy(p, Data, Length);
- size = Length + (p - data);
+ size = Length + (p - buffer);
}
else
- esyslog("ERROR: invalid data length for TPDU tag 0x%02X: %d", Tag, Length);
+ esyslog("ERROR: invalid data length for TPDU tag 0x%02X: %d (%d/%d)", Tag, Length, Slot, Tcid);
break;
default:
- esyslog("ERROR: unknown TPDU tag: 0x%02X", Tag);
+ esyslog("ERROR: unknown TPDU tag: 0x%02X (%d/%d)", Tag, Slot, Tcid);
}
}
-int cTPDU::Write(int fd)
-{
- Dump(fd, true);
- if (size)
- return safe_write(fd, data, size) == size ? OK : ERROR;
- esyslog("ERROR: attemp to write TPDU with zero size");
- return ERROR;
-}
-
-int cTPDU::Read(int fd)
+void cTPDU::Dump(int SlotNumber, bool Outgoing)
{
- size = safe_read(fd, data, sizeof(data));
- if (size < 0) {
- esyslog("ERROR: %m");
- size = 0;
- return ERROR;
- }
- Dump(fd, false);
- return OK;
-}
-
-void cTPDU::Dump(int fd, bool Outgoing)
-{
- if (DumpTPDUDataTransfer) {
+ if (DumpTPDUDataTransfer && (DumpPolls || Tag() != T_SB)) {
#define MAX_DUMP 256
- fprintf(stderr, "%2d %s ", fd, Outgoing ? "-->" : "<--");
+ fprintf(stderr, " %d: %s ", SlotNumber, Outgoing ? "-->" : "<--");
for (int i = 0; i < size && i < MAX_DUMP; i++)
- fprintf(stderr, "%02X ", data[i]);
+ fprintf(stderr, "%02X ", buffer[i]);
fprintf(stderr, "%s\n", size >= MAX_DUMP ? "..." : "");
if (!Outgoing) {
- fprintf(stderr, " ");
+ fprintf(stderr, " ");
for (int i = 0; i < size && i < MAX_DUMP; i++)
- fprintf(stderr, "%2c ", isprint(data[i]) ? data[i] : '.');
+ fprintf(stderr, "%2c ", isprint(buffer[i]) ? buffer[i] : '.');
fprintf(stderr, "%s\n", size >= MAX_DUMP ? "..." : "");
}
}
@@ -257,301 +218,51 @@ const uint8_t *cTPDU::GetData(const uint8_t *Data, int &Length)
uint8_t cTPDU::Status(void)
{
- if (size >= 4 && data[size - 4] == T_SB && data[size - 3] == 2) {
- //XXX test tcid???
- return data[size - 1];
- }
+ if (size >= 4 && buffer[size - 4] == T_SB && buffer[size - 3] == 2)
+ return buffer[size - 1];
return 0;
}
// --- cCiTransportConnection ------------------------------------------------
-enum eState { stIDLE, stCREATION, stACTIVE, stDELETION };
+#define MAX_SESSIONS_PER_TC 16
class cCiTransportConnection {
- friend class cCiTransportLayer;
private:
- int fd;
- uint8_t slot;
+ enum eState { stIDLE, stCREATION, stACTIVE, stDELETION };
+ cCamSlot *camSlot;
uint8_t tcid;
eState state;
- cTPDU *tpdu;
- int lastResponse;
- bool dataAvailable;
- void Init(int Fd, uint8_t Slot, uint8_t Tcid);
- int SendTPDU(uint8_t Tag, int Length = 0, const uint8_t *Data = NULL);
- int RecvTPDU(void);
- int CreateConnection(void);
- int Poll(void);
- eState State(void) { return state; }
- int LastResponse(void) { return lastResponse; }
- bool DataAvailable(void) { return dataAvailable; }
-public:
- cCiTransportConnection(void);
- ~cCiTransportConnection();
- int Slot(void) const { return slot; }
- int SendData(int Length, const uint8_t *Data);
- int RecvData(void);
- const uint8_t *Data(int &Length);
- //XXX Close()
- void Reset(void);
- };
-
-cCiTransportConnection::cCiTransportConnection(void)
-{
- tpdu = NULL;
- Init(-1, 0, 0);
-}
-
-cCiTransportConnection::~cCiTransportConnection()
-{
- delete tpdu;
-}
-
-void cCiTransportConnection::Init(int Fd, uint8_t Slot, uint8_t Tcid)
-{
- fd = Fd;
- slot = Slot;
- tcid = Tcid;
- state = stIDLE;
- if (fd >= 0 && !tpdu)
- tpdu = new cTPDU;
- lastResponse = ERROR;
- dataAvailable = false;
-//XXX Clear()???
-}
-
-int cCiTransportConnection::SendTPDU(uint8_t Tag, int Length, const uint8_t *Data)
-{
- cTPDU TPDU(slot, tcid, Tag, Length, Data);
- return TPDU.Write(fd);
-}
-
-#define CAM_READ_TIMEOUT 3500 // ms
-
-int cCiTransportConnection::RecvTPDU(void)
-{
- struct pollfd pfd[1];
- pfd[0].fd = fd;
- pfd[0].events = POLLIN;
- lastResponse = ERROR;
- if (poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN) && tpdu->Read(fd) == OK && tpdu->Tcid() == tcid) {
- switch (state) {
- case stIDLE: break;
- case stCREATION: if (tpdu->Tag() == T_CTC_REPLY) {
- dataAvailable = tpdu->Status() & DATA_INDICATOR;
- state = stACTIVE;
- lastResponse = tpdu->Tag();
- }
- break;
- case stACTIVE: switch (tpdu->Tag()) {
- case T_SB:
- case T_DATA_LAST:
- case T_DATA_MORE:
- case T_REQUEST_TC: break;
- case T_DELETE_TC: if (SendTPDU(T_DTC_REPLY) != OK)
- return ERROR;
- Init(fd, slot, tcid);
- break;
- default: return ERROR;
- }
- dataAvailable = tpdu->Status() & DATA_INDICATOR;
- lastResponse = tpdu->Tag();
- break;
- case stDELETION: if (tpdu->Tag() == T_DTC_REPLY) {
- Init(fd, slot, tcid);
- //XXX Status()???
- lastResponse = tpdu->Tag();
- }
- break;
- }
- }
- else if (FATALERRNO) {
- esyslog("ERROR: CAM: Read failed: slot %d, tcid %d - %m", slot, tcid);
- Init(-1, slot, tcid);
- }
- return lastResponse;
-}
-
-int cCiTransportConnection::SendData(int Length, const uint8_t *Data)
-{
- while (state == stACTIVE && Length > 0) {
- uint8_t Tag = T_DATA_LAST;
- int l = Length;
- if (l > MAX_TPDU_DATA) {
- Tag = T_DATA_MORE;
- l = MAX_TPDU_DATA;
- }
- if (SendTPDU(Tag, l, Data) != OK || RecvTPDU() != T_SB)
- break;
- Length -= l;
- Data += l;
- }
- return Length ? ERROR : OK;
-}
-
-int cCiTransportConnection::RecvData(void)
-{
- if (SendTPDU(T_RCV) == OK)
- return RecvTPDU();
- return ERROR;
-}
-
-const uint8_t *cCiTransportConnection::Data(int &Length)
-{
- return tpdu->Data(Length);
-}
-
-#define MAX_CONNECT_RETRIES 2
-
-int cCiTransportConnection::CreateConnection(void)
-{
- if (state == stIDLE) {
- if (SendTPDU(T_CREATE_TC) == OK) {
- state = stCREATION;
- if (RecvTPDU() == T_CTC_REPLY)
- return OK;
- // the following is a workaround for CAMs that don't quite follow the specs...
- else {
- for (int i = 0; i < MAX_CONNECT_RETRIES; i++) {
- dsyslog("CAM: retrying to establish connection");
- if (RecvTPDU() == T_CTC_REPLY) {
- dsyslog("CAM: connection established");
- return OK;
- }
- }
- return ERROR;
- }
- }
- }
- return ERROR;
-}
-
-int cCiTransportConnection::Poll(void)
-{
- if (state == stACTIVE) {
- if (SendTPDU(T_DATA_LAST) == OK)
- return RecvTPDU();
- }
- return ERROR;
-}
-
-void cCiTransportConnection::Reset(void)
-{
- Init(-1, 0, 0);
-}
-
-// --- cCiTransportLayer -----------------------------------------------------
-
-#define MAX_CI_CONNECT 16 // maximum possible value is 254
-
-class cCiTransportLayer {
-private:
- int fd;
- int numSlots;
- cCiTransportConnection tc[MAX_CI_CONNECT];
+ bool createConnectionRequested;
+ bool deleteConnectionRequested;
+ bool hasUserIO;
+ cTimeMs alive;
+ cTimeMs timer;
+ cCiSession *sessions[MAX_SESSIONS_PER_TC + 1]; // session numbering starts with 1
+ void SendTPDU(uint8_t Tag, int Length = 0, const uint8_t *Data = NULL);
+ void SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId = 0, int Status = -1);
+ void Poll(void);
+ uint32_t ResourceIdToInt(const uint8_t *Data);
+ cCiSession *GetSessionBySessionId(uint16_t SessionId);
+ void OpenSession(int Length, const uint8_t *Data);
+ void CloseSession(uint16_t SessionId);
+ void HandleSessions(cTPDU *TPDU);
public:
- cCiTransportLayer(int Fd, int NumSlots);
- cCiTransportConnection *NewConnection(int Slot);
- bool ResetSlot(int Slot, bool Wait = false);
- bool ModuleReady(int Slot);
- cCiTransportConnection *Process(int Slot);
+ cCiTransportConnection(cCamSlot *CamSlot, uint8_t Tcid);
+ virtual ~cCiTransportConnection();
+ cCamSlot *CamSlot(void) { return camSlot; }
+ uint8_t Tcid(void) const { return tcid; }
+ void CreateConnection(void) { createConnectionRequested = true; }
+ void DeleteConnection(void) { deleteConnectionRequested = true; }
+ const char *GetCamName(void);
+ bool Ready(void);
+ bool HasUserIO(void) { return hasUserIO; }
+ void SendData(int Length, const uint8_t *Data);
+ bool Process(cTPDU *TPDU = NULL);
+ cCiSession *GetSessionByResourceId(uint32_t ResourceId);
};
-cCiTransportLayer::cCiTransportLayer(int Fd, int NumSlots)
-{
- fd = Fd;
- numSlots = NumSlots;
- for (int s = 0; s < numSlots; s++)
- ResetSlot(s);
- cCondWait::SleepMs(2000);
-}
-
-cCiTransportConnection *cCiTransportLayer::NewConnection(int Slot)
-{
- for (int i = 0; i < MAX_CI_CONNECT; i++) {
- if (tc[i].State() == stIDLE) {
- dbgprotocol("Creating connection: slot %d, tcid %d\n", Slot, i + 1);
- tc[i].Init(fd, Slot, i + 1);
- if (tc[i].CreateConnection() == OK)
- return &tc[i];
- break;
- }
- }
- return NULL;
-}
-
-bool cCiTransportLayer::ResetSlot(int Slot, bool Wait)
-{
- for (int i = 0; i < MAX_CI_CONNECT; i++) {
- if (tc[i].State() != stIDLE && tc[i].Slot() == Slot)
- tc[i].Reset();
- }
- dbgprotocol("Resetting slot %d...", Slot);
- if (ioctl(fd, CA_RESET, 1 << Slot) != -1) {
- if (Wait)
- cCondWait::SleepMs(2000);
- dbgprotocol("ok.\n");
- return true;
- }
- else
- esyslog("ERROR: can't reset CAM slot %d: %m", Slot);
- dbgprotocol("failed!\n");
- return false;
-}
-
-bool cCiTransportLayer::ModuleReady(int Slot)
-{
- ca_slot_info_t sinfo;
- sinfo.num = Slot;
- if (ioctl(fd, CA_GET_SLOT_INFO, &sinfo) != -1)
- return sinfo.flags & CA_CI_MODULE_READY;
- else
- esyslog("ERROR: can't get info on CAM slot %d: %m", Slot);
- return false;
-}
-
-cCiTransportConnection *cCiTransportLayer::Process(int Slot)
-{
- for (int i = 0; i < MAX_CI_CONNECT; i++) {
- cCiTransportConnection *Tc = &tc[i];
- if (Tc->Slot() == Slot) {
- switch (Tc->State()) {
- case stCREATION:
- case stACTIVE:
- if (!Tc->DataAvailable()) {
- if (Tc->Poll() != OK)
- ;//XXX continue;
- }
- switch (Tc->LastResponse()) {
- case T_REQUEST_TC:
- //XXX
- break;
- case T_DATA_MORE:
- case T_DATA_LAST:
- case T_CTC_REPLY:
- case T_SB:
- if (Tc->DataAvailable())
- Tc->RecvData();
- break;
- case TIMEOUT:
- case ERROR:
- default:
- //XXX Tc->state = stIDLE;//XXX Init()???
- return NULL;
- break;
- }
- //XXX this will only work with _one_ transport connection per slot!
- return Tc;
- break;
- default: ;
- }
- }
- }
- return NULL;
-}
-
-// -- cCiSession -------------------------------------------------------------
+// --- cCiSession ------------------------------------------------------------
// Session Tags:
@@ -636,15 +347,15 @@ private:
protected:
int GetTag(int &Length, const uint8_t **Data);
const uint8_t *GetData(const uint8_t *Data, int &Length);
- int SendData(int Tag, int Length = 0, const uint8_t *Data = NULL);
+ void SendData(int Tag, int Length = 0, const uint8_t *Data = NULL);
+ cCiTransportConnection *Tc(void) { return tc; }
public:
cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc);
virtual ~cCiSession();
- const cCiTransportConnection *Tc(void) { return tc; }
uint16_t SessionId(void) { return sessionId; }
uint32_t ResourceId(void) { return resourceId; }
virtual bool HasUserIO(void) { return false; }
- virtual bool Process(int Length = 0, const uint8_t *Data = NULL);
+ virtual void Process(int Length = 0, const uint8_t *Data = NULL);
};
cCiSession::cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc)
@@ -679,7 +390,7 @@ const uint8_t *cCiSession::GetData(const uint8_t *Data, int &Length)
return Length ? Data : NULL;
}
-int cCiSession::SendData(int Tag, int Length, const uint8_t *Data)
+void cCiSession::SendData(int Tag, int Length, const uint8_t *Data)
{
uint8_t buffer[2048];
uint8_t *p = buffer;
@@ -694,78 +405,75 @@ int cCiSession::SendData(int Tag, int Length, const uint8_t *Data)
if (p - buffer + Length < int(sizeof(buffer))) {
memcpy(p, Data, Length);
p += Length;
- return tc->SendData(p - buffer, buffer);
+ tc->SendData(p - buffer, buffer);
}
- esyslog("ERROR: CAM: data length (%d) exceeds buffer size", Length);
- return ERROR;
+ else
+ esyslog("ERROR: CAM %d: data length (%d) exceeds buffer size", Tc()->CamSlot()->SlotNumber(), Length);
}
-bool cCiSession::Process(int Length, const uint8_t *Data)
+void cCiSession::Process(int Length, const uint8_t *Data)
{
- return true;
}
-// -- cCiResourceManager -----------------------------------------------------
+// --- cCiResourceManager ----------------------------------------------------
class cCiResourceManager : public cCiSession {
private:
int state;
public:
cCiResourceManager(uint16_t SessionId, cCiTransportConnection *Tc);
- virtual bool Process(int Length = 0, const uint8_t *Data = NULL);
+ virtual void Process(int Length = 0, const uint8_t *Data = NULL);
};
cCiResourceManager::cCiResourceManager(uint16_t SessionId, cCiTransportConnection *Tc)
:cCiSession(SessionId, RI_RESOURCE_MANAGER, Tc)
{
- dbgprotocol("New Resource Manager (session id %d)\n", SessionId);
+ dbgprotocol("Slot %d: new Resource Manager (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
state = 0;
}
-bool cCiResourceManager::Process(int Length, const uint8_t *Data)
+void cCiResourceManager::Process(int Length, const uint8_t *Data)
{
if (Data) {
int Tag = GetTag(Length, &Data);
switch (Tag) {
case AOT_PROFILE_ENQ: {
- dbgprotocol("%d: <== Profile Enquiry\n", SessionId());
+ dbgprotocol("Slot %d: <== Profile Enquiry (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
uint32_t resources[] = { htonl(RI_RESOURCE_MANAGER),
htonl(RI_APPLICATION_INFORMATION),
htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
htonl(RI_DATE_TIME),
htonl(RI_MMI)
};
- dbgprotocol("%d: ==> Profile\n", SessionId());
+ dbgprotocol("Slot %d: ==> Profile (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
SendData(AOT_PROFILE, sizeof(resources), (uint8_t*)resources);
state = 3;
}
break;
case AOT_PROFILE: {
- dbgprotocol("%d: <== Profile\n", SessionId());
+ dbgprotocol("Slot %d: <== Profile (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
if (state == 1) {
int l = 0;
const uint8_t *d = GetData(Data, l);
if (l > 0 && d)
- esyslog("CI resource manager: unexpected data");
- dbgprotocol("%d: ==> Profile Change\n", SessionId());
+ esyslog("ERROR: CAM %d: resource manager: unexpected data", Tc()->CamSlot()->SlotNumber());
+ dbgprotocol("Slot %d: ==> Profile Change (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
SendData(AOT_PROFILE_CHANGE);
state = 2;
}
else {
- esyslog("ERROR: CI resource manager: unexpected tag %06X in state %d", Tag, state);
+ esyslog("ERROR: CAM %d: resource manager: unexpected tag %06X in state %d", Tc()->CamSlot()->SlotNumber(), Tag, state);
}
}
break;
- default: esyslog("ERROR: CI resource manager: unknown tag %06X", Tag);
- return false;
+ default: esyslog("ERROR: CAM %d: resource manager: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
}
}
else if (state == 0) {
- dbgprotocol("%d: ==> Profile Enq\n", SessionId());
+ dbgprotocol("Slot %d: ==> Profile Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
SendData(AOT_PROFILE_ENQ);
state = 1;
}
- return true;
}
// --- cCiApplicationInformation ---------------------------------------------
@@ -773,7 +481,6 @@ bool cCiResourceManager::Process(int Length, const uint8_t *Data)
class cCiApplicationInformation : public cCiSession {
private:
int state;
- time_t creationTime;
uint8_t applicationType;
uint16_t applicationManufacturer;
uint16_t manufacturerCode;
@@ -781,7 +488,7 @@ private:
public:
cCiApplicationInformation(uint16_t SessionId, cCiTransportConnection *Tc);
virtual ~cCiApplicationInformation();
- virtual bool Process(int Length = 0, const uint8_t *Data = NULL);
+ virtual void Process(int Length = 0, const uint8_t *Data = NULL);
bool EnterMenu(void);
const char *GetMenuString(void) { return menuString; }
};
@@ -789,9 +496,8 @@ public:
cCiApplicationInformation::cCiApplicationInformation(uint16_t SessionId, cCiTransportConnection *Tc)
:cCiSession(SessionId, RI_APPLICATION_INFORMATION, Tc)
{
- dbgprotocol("New Application Information (session id %d)\n", SessionId);
+ dbgprotocol("Slot %d: new Application Information (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
state = 0;
- creationTime = time(NULL);
menuString = NULL;
}
@@ -800,13 +506,13 @@ cCiApplicationInformation::~cCiApplicationInformation()
free(menuString);
}
-bool cCiApplicationInformation::Process(int Length, const uint8_t *Data)
+void cCiApplicationInformation::Process(int Length, const uint8_t *Data)
{
if (Data) {
int Tag = GetTag(Length, &Data);
switch (Tag) {
case AOT_APPLICATION_INFO: {
- dbgprotocol("%d: <== Application Info\n", SessionId());
+ dbgprotocol("Slot %d: <== Application Info (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
int l = 0;
const uint8_t *d = GetData(Data, l);
if ((l -= 1) < 0) break;
@@ -819,28 +525,26 @@ bool cCiApplicationInformation::Process(int Length, const uint8_t *Data)
d += 2;
free(menuString);
menuString = GetString(l, &d);
- isyslog("CAM: %s, %02X, %04X, %04X", menuString, applicationType, applicationManufacturer, manufacturerCode);//XXX make externally accessible!
- }
+ isyslog("CAM %d: %s, %02X, %04X, %04X", Tc()->CamSlot()->SlotNumber(), menuString, applicationType, applicationManufacturer, manufacturerCode);
state = 2;
+ }
break;
- default: esyslog("ERROR: CI application information: unknown tag %06X", Tag);
- return false;
+ default: esyslog("ERROR: CAM %d: application information: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
}
}
else if (state == 0) {
- dbgprotocol("%d: ==> Application Info Enq\n", SessionId());
+ dbgprotocol("Slot %d: ==> Application Info Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
SendData(AOT_APPLICATION_INFO_ENQ);
state = 1;
}
- return true;
}
bool cCiApplicationInformation::EnterMenu(void)
{
- if (state == 2 && time(NULL) - creationTime > WRKRND_TIME_BEFORE_ENTER_MENU) {
- dbgprotocol("%d: ==> Enter Menu\n", SessionId());
+ if (state == 2) {
+ dbgprotocol("Slot %d: ==> Enter Menu (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
SendData(AOT_ENTER_MENU);
- return true;//XXX
+ return true;
}
return false;
}
@@ -875,13 +579,14 @@ private:
bool streamFlag;
void AddCaDescriptors(int Length, const uint8_t *Data);
public:
- cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const unsigned short *CaSystemIds);
+ cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds);
+ uint8_t CmdId(void) { return cmdId; }
void SetListManagement(uint8_t ListManagement);
- bool Valid(void);
+ uint8_t ListManagement(void) { return capmt[0]; }
void AddPid(int Pid, uint8_t StreamType);
};
-cCiCaPmt::cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const unsigned short *CaSystemIds)
+cCiCaPmt::cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds)
{
cmdId = CmdId;
caDescriptorsLength = GetCaDescriptors(Source, Transponder, ProgramNumber, CaSystemIds, sizeof(caDescriptors), caDescriptors, streamFlag);
@@ -902,11 +607,6 @@ void cCiCaPmt::SetListManagement(uint8_t ListManagement)
capmt[0] = ListManagement;
}
-bool cCiCaPmt::Valid(void)
-{
- return caDescriptorsLength > 0;
-}
-
void cCiCaPmt::AddPid(int Pid, uint8_t StreamType)
{
if (Pid) {
@@ -957,61 +657,77 @@ void cCiCaPmt::AddCaDescriptors(int Length, const uint8_t *Data)
#define CA_ENABLE(x) (((x) & CA_ENABLE_FLAG) ? (x) & ~CA_ENABLE_FLAG : 0)
+#define QUERY_WAIT_TIME 1000 // ms to wait before sending a query
+#define QUERY_REPLY_TIMEOUT 2000 // ms to wait for a reply to a query
+
class cCiConditionalAccessSupport : public cCiSession {
private:
int state;
int numCaSystemIds;
- unsigned short caSystemIds[MAXCASYSTEMIDS + 1]; // list is zero terminated!
+ int caSystemIds[MAXCASYSTEMIDS + 1]; // list is zero terminated!
+ bool repliesToQuery;
+ cTimeMs timer;
public:
cCiConditionalAccessSupport(uint16_t SessionId, cCiTransportConnection *Tc);
- virtual bool Process(int Length = 0, const uint8_t *Data = NULL);
- const unsigned short *GetCaSystemIds(void) { return caSystemIds; }
- bool SendPMT(cCiCaPmt *CaPmt);
- bool ReceivedReply(bool CanDescramble = false);
+ virtual void Process(int Length = 0, const uint8_t *Data = NULL);
+ const int *GetCaSystemIds(void) { return caSystemIds; }
+ void SendPMT(cCiCaPmt *CaPmt);
+ bool RepliesToQuery(void) { return repliesToQuery; }
+ bool Ready(void) { return state >= 4; }
+ bool ReceivedReply(void) { return state >= 5; }
+ bool CanDecrypt(void) { return state == 6; }
};
cCiConditionalAccessSupport::cCiConditionalAccessSupport(uint16_t SessionId, cCiTransportConnection *Tc)
:cCiSession(SessionId, RI_CONDITIONAL_ACCESS_SUPPORT, Tc)
{
- dbgprotocol("New Conditional Access Support (session id %d)\n", SessionId);
+ dbgprotocol("Slot %d: new Conditional Access Support (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
state = 0; // inactive
caSystemIds[numCaSystemIds = 0] = 0;
+ repliesToQuery = false;
}
-bool cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
+void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
{
if (Data) {
int Tag = GetTag(Length, &Data);
switch (Tag) {
case AOT_CA_INFO: {
- dbgprotocol("%d: <== Ca Info", SessionId());
+ dbgprotocol("Slot %d: <== Ca Info (%d)", Tc()->CamSlot()->SlotNumber(), SessionId());
numCaSystemIds = 0;
int l = 0;
const uint8_t *d = GetData(Data, l);
while (l > 1) {
- unsigned short id = ((unsigned short)(*d) << 8) | *(d + 1);
+ uint16_t id = ((uint16_t)(*d) << 8) | *(d + 1);
dbgprotocol(" %04X", id);
d += 2;
l -= 2;
if (numCaSystemIds < MAXCASYSTEMIDS)
caSystemIds[numCaSystemIds++] = id;
else {
- esyslog("ERROR: too many CA system IDs!");
+ esyslog("ERROR: CAM %d: too many CA system IDs!", Tc()->CamSlot()->SlotNumber());
break;
}
}
caSystemIds[numCaSystemIds] = 0;
dbgprotocol("\n");
+ if (state == 1) {
+ timer.Set(QUERY_WAIT_TIME); // WORKAROUND: Alphacrypt 3.09 doesn't reply to QUERY immediately after reset
+ state = 2; // got ca info
+ }
}
- state = 2; // got ca info
break;
case AOT_CA_PMT_REPLY: {
- dbgprotocol("%d: <== Ca Pmt Reply", SessionId());
- state = 4; // got ca pmt reply
+ dbgprotocol("Slot %d: <== Ca Pmt Reply (%d)", Tc()->CamSlot()->SlotNumber(), SessionId());
+ if (!repliesToQuery) {
+ dsyslog("CAM %d: replies to QUERY - multi channel decryption possible", Tc()->CamSlot()->SlotNumber());
+ repliesToQuery = true;
+ }
+ state = 5; // got ca pmt reply
int l = 0;
const uint8_t *d = GetData(Data, l);
if (l > 1) {
- unsigned short pnr = ((unsigned short)(*d) << 8) | *(d + 1);
+ uint16_t pnr = ((uint16_t)(*d) << 8) | *(d + 1);
dbgprotocol(" %d", pnr);
d += 2;
l -= 2;
@@ -1026,7 +742,7 @@ bool cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
// CAMs (for instance the AlphaCrypt with firmware <= 3.05) that
// insert a two byte length field here.
// This is a workaround to skip this length field:
- unsigned short len = ((unsigned short)(*d) << 8) | *(d + 1);
+ uint16_t len = ((uint16_t)(*d) << 8) | *(d + 1);
if (len == l - 2) {
d += 2;
l -= 2;
@@ -1040,7 +756,7 @@ bool cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
if (l <= 2)
ok = CA_ENABLE(caepl) == CAEI_POSSIBLE;
while (l > 2) {
- unsigned short pid = ((unsigned short)(*d) << 8) | *(d + 1);
+ uint16_t pid = ((uint16_t)(*d) << 8) | *(d + 1);
unsigned char caees = *(d + 2);
dbgprotocol(" %d=%02X", pid, caees);
d += 3;
@@ -1049,39 +765,40 @@ bool cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
ok = false;
}
if (ok)
- state = 5; // descrambling possible
+ state = 6; // descrambling possible
}
}
}
dbgprotocol("\n");
}
break;
- default: esyslog("ERROR: CI conditional access support: unknown tag %06X", Tag);
- return false;
+ default: esyslog("ERROR: CAM %d: conditional access support: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
}
}
else if (state == 0) {
- dbgprotocol("%d: ==> Ca Info Enq\n", SessionId());
+ dbgprotocol("Slot %d: ==> Ca Info Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
SendData(AOT_CA_INFO_ENQ);
state = 1; // enquired ca info
}
- return true;
+ else if (state == 2 && timer.TimedOut()) {
+ cCiCaPmt CaPmt(CPCI_QUERY, 0, 0, 0, NULL);
+ SendPMT(&CaPmt);
+ timer.Set(QUERY_REPLY_TIMEOUT);
+ state = 3; // waiting for reply
+ }
+ else if (state == 3 && timer.TimedOut()) {
+ dsyslog("CAM %d: doesn't reply to QUERY - only a single channel can be decrypted", Tc()->CamSlot()->SlotNumber());
+ state = 4; // normal operation
+ }
}
-bool cCiConditionalAccessSupport::SendPMT(cCiCaPmt *CaPmt)
+void cCiConditionalAccessSupport::SendPMT(cCiCaPmt *CaPmt)
{
if (CaPmt && state >= 2) {
- dbgprotocol("%d: ==> Ca Pmt\n", SessionId());
+ dbgprotocol("Slot %d: ==> Ca Pmt (%d) %d %d\n", Tc()->CamSlot()->SlotNumber(), SessionId(), CaPmt->ListManagement(), CaPmt->CmdId());
SendData(AOT_CA_PMT, CaPmt->length, CaPmt->capmt);
- state = 3; // sent ca pmt
- return true;
+ state = 4; // sent ca pmt
}
- return false;
-}
-
-bool cCiConditionalAccessSupport::ReceivedReply(bool CanDescramble)
-{
- return state >= (CanDescramble ? 5 : 4);
}
// --- cCiDateTime -----------------------------------------------------------
@@ -1090,10 +807,10 @@ class cCiDateTime : public cCiSession {
private:
int interval;
time_t lastTime;
- bool SendDateTime(void);
+ void SendDateTime(void);
public:
cCiDateTime(uint16_t SessionId, cCiTransportConnection *Tc);
- virtual bool Process(int Length = 0, const uint8_t *Data = NULL);
+ virtual void Process(int Length = 0, const uint8_t *Data = NULL);
};
cCiDateTime::cCiDateTime(uint16_t SessionId, cCiTransportConnection *Tc)
@@ -1101,10 +818,10 @@ cCiDateTime::cCiDateTime(uint16_t SessionId, cCiTransportConnection *Tc)
{
interval = 0;
lastTime = 0;
- dbgprotocol("New Date Time (session id %d)\n", SessionId);
+ dbgprotocol("Slot %d: new Date Time (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
}
-bool cCiDateTime::SendDateTime(void)
+void cCiDateTime::SendDateTime(void)
{
time_t t = time(NULL);
struct tm tm_gmt;
@@ -1116,17 +833,18 @@ bool cCiDateTime::SendDateTime(void)
int L = (M == 1 || M == 2) ? 1 : 0;
int MJD = 14956 + D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001);
#define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
- struct tTime { unsigned short mjd; uint8_t h, m, s; short offset; };
+ struct tTime { uint16_t mjd; uint8_t h, m, s; short offset; };
tTime T = { mjd : htons(MJD), h : DEC2BCD(tm_gmt.tm_hour), m : DEC2BCD(tm_gmt.tm_min), s : DEC2BCD(tm_gmt.tm_sec), offset : htons(tm_loc.tm_gmtoff / 60) };
- dbgprotocol("%d: ==> Date Time\n", SessionId());
+ bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer;
+ DumpTPDUDataTransfer &= DumpDateTime;
+ if (DumpDateTime)
+ dbgprotocol("Slot %d: ==> Date Time (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
SendData(AOT_DATE_TIME, 7, (uint8_t*)&T);
- //XXX return value of all SendData() calls???
- return true;
+ DumpTPDUDataTransfer = OldDumpTPDUDataTransfer;
}
- return false;
}
-bool cCiDateTime::Process(int Length, const uint8_t *Data)
+void cCiDateTime::Process(int Length, const uint8_t *Data)
{
if (Data) {
int Tag = GetTag(Length, &Data);
@@ -1137,20 +855,18 @@ bool cCiDateTime::Process(int Length, const uint8_t *Data)
const uint8_t *d = GetData(Data, l);
if (l > 0)
interval = *d;
- dbgprotocol("%d: <== Date Time Enq, interval = %d\n", SessionId(), interval);
+ dbgprotocol("Slot %d: <== Date Time Enq (%d), interval = %d\n", Tc()->CamSlot()->SlotNumber(), SessionId(), interval);
lastTime = time(NULL);
- return SendDateTime();
+ SendDateTime();
}
break;
- default: esyslog("ERROR: CI date time: unknown tag %06X", Tag);
- return false;
+ default: esyslog("ERROR: CAM %d: date time: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
}
}
else if (interval && time(NULL) - lastTime > interval) {
lastTime = time(NULL);
- return SendDateTime();
+ SendDateTime();
}
- return true;
}
// --- cCiMMI ----------------------------------------------------------------
@@ -1197,11 +913,11 @@ private:
public:
cCiMMI(uint16_t SessionId, cCiTransportConnection *Tc);
virtual ~cCiMMI();
- virtual bool Process(int Length = 0, const uint8_t *Data = NULL);
+ virtual void Process(int Length = 0, const uint8_t *Data = NULL);
virtual bool HasUserIO(void) { return menu || enquiry; }
cCiMenu *Menu(bool Clear = false);
cCiEnquiry *Enquiry(bool Clear = false);
- bool SendMenuAnswer(uint8_t Selection);
+ void SendMenuAnswer(uint8_t Selection);
bool SendAnswer(const char *Text);
bool SendCloseMMI(void);
};
@@ -1209,7 +925,7 @@ public:
cCiMMI::cCiMMI(uint16_t SessionId, cCiTransportConnection *Tc)
:cCiSession(SessionId, RI_MMI, Tc)
{
- dbgprotocol("New MMI (session id %d)\n", SessionId);
+ dbgprotocol("Slot %d: new MMI (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
menu = fetchedMenu = NULL;
enquiry = fetchedEnquiry = NULL;
}
@@ -1236,21 +952,21 @@ char *cCiMMI::GetText(int &Length, const uint8_t **Data)
int Tag = GetTag(Length, Data);
if (Tag == AOT_TEXT_LAST) {
char *s = GetString(Length, Data);
- dbgprotocol("%d: <== Text Last '%s'\n", SessionId(), s);
+ dbgprotocol("Slot %d: <== Text Last (%d) '%s'\n", Tc()->CamSlot()->SlotNumber(), SessionId(), s);
return s;
}
else
- esyslog("CI MMI: unexpected text tag: %06X", Tag);
+ esyslog("ERROR: CAM %d: MMI: unexpected text tag: %06X", Tc()->CamSlot()->SlotNumber(), Tag);
return NULL;
}
-bool cCiMMI::Process(int Length, const uint8_t *Data)
+void cCiMMI::Process(int Length, const uint8_t *Data)
{
if (Data) {
int Tag = GetTag(Length, &Data);
switch (Tag) {
case AOT_DISPLAY_CONTROL: {
- dbgprotocol("%d: <== Display Control\n", SessionId());
+ dbgprotocol("Slot %d: <== Display Control (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
int l = 0;
const uint8_t *d = GetData(Data, l);
if (l > 0) {
@@ -1259,19 +975,18 @@ bool cCiMMI::Process(int Length, const uint8_t *Data)
if (l == 2 && *++d == MM_HIGH_LEVEL) {
struct tDisplayReply { uint8_t id; uint8_t mode; };
tDisplayReply dr = { id : DRI_MMI_MODE_ACK, mode : MM_HIGH_LEVEL };
- dbgprotocol("%d: ==> Display Reply\n", SessionId());
+ dbgprotocol("Slot %d: ==> Display Reply (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
SendData(AOT_DISPLAY_REPLY, 2, (uint8_t *)&dr);
}
break;
- default: esyslog("CI MMI: unsupported display control command %02X", *d);
- return false;
+ default: esyslog("ERROR: CAM %d: MMI: unsupported display control command %02X", Tc()->CamSlot()->SlotNumber(), *d);
}
}
}
break;
case AOT_LIST_LAST:
case AOT_MENU_LAST: {
- dbgprotocol("%d: <== Menu Last\n", SessionId());
+ dbgprotocol("Slot %d: <== Menu Last (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
delete menu;
menu = new cCiMenu(this, Tag == AOT_MENU_LAST);
int l = 0;
@@ -1296,7 +1011,7 @@ bool cCiMMI::Process(int Length, const uint8_t *Data)
}
break;
case AOT_ENQ: {
- dbgprotocol("%d: <== Enq\n", SessionId());
+ dbgprotocol("Slot %d: <== Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
delete enquiry;
enquiry = new cCiEnquiry(this);
int l = 0;
@@ -1323,14 +1038,12 @@ bool cCiMMI::Process(int Length, const uint8_t *Data)
if (l > 1)
delay = *d;
}
- dbgprotocol("%d: <== Close MMI id = %02X delay = %d\n", SessionId(), id, delay);
+ dbgprotocol("Slot %d: <== Close MMI (%d) id = %02X delay = %d\n", Tc()->CamSlot()->SlotNumber(), SessionId(), id, delay);
}
break;
- default: esyslog("ERROR: CI MMI: unknown tag %06X", Tag);
- return false;
+ default: esyslog("ERROR: CAM %d: MMI: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
}
}
- return true;
}
cCiMenu *cCiMMI::Menu(bool Clear)
@@ -1355,32 +1068,28 @@ cCiEnquiry *cCiMMI::Enquiry(bool Clear)
return fetchedEnquiry;
}
-bool cCiMMI::SendMenuAnswer(uint8_t Selection)
+void cCiMMI::SendMenuAnswer(uint8_t Selection)
{
- dbgprotocol("%d: ==> Menu Answ\n", SessionId());
+ dbgprotocol("Slot %d: ==> Menu Answ (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
SendData(AOT_MENU_ANSW, 1, &Selection);
- //XXX return value of all SendData() calls???
- return true;
}
bool cCiMMI::SendAnswer(const char *Text)
{
- dbgprotocol("%d: ==> Answ\n", SessionId());
+ dbgprotocol("Slot %d: ==> Answ (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
struct tAnswer { uint8_t id; char text[256]; };//XXX
tAnswer answer;
answer.id = Text ? AI_ANSWER : AI_CANCEL;
if (Text)
strncpy(answer.text, Text, sizeof(answer.text));
SendData(AOT_ANSW, Text ? strlen(Text) + 1 : 1, (uint8_t *)&answer);
- //XXX return value of all SendData() calls???
return true;
}
bool cCiMMI::SendCloseMMI(void)
{
- dbgprotocol("%d: ==> Close MMI\n", SessionId());
+ dbgprotocol("Slot %d: ==> Close MMI (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
SendData(AOT_CLOSE_MMI, 0);
- //XXX return value of all SendData() calls???
return true;
}
@@ -1422,24 +1131,23 @@ bool cCiMenu::HasUpdate(void)
return !mmi || mmi->HasUserIO();
}
-bool cCiMenu::Select(int Index)
+void cCiMenu::Select(int Index)
{
cMutexLock MutexLock(mutex);
- dbgprotocol("%d: ==> Select %d\n", mmi ? mmi->SessionId() : -1, Index);
if (mmi && -1 <= Index && Index < numEntries)
- return mmi->SendMenuAnswer(Index + 1);
- return false;
+ mmi->SendMenuAnswer(Index + 1);
}
-bool cCiMenu::Cancel(void)
+void cCiMenu::Cancel(void)
{
- return Select(-1);
+ Select(-1);
}
-bool cCiMenu::Abort(void)
+void cCiMenu::Abort(void)
{
cMutexLock MutexLock(mutex);
- return mmi && mmi->SendCloseMMI();
+ if (mmi)
+ mmi->SendCloseMMI();
}
// --- cCiEnquiry ------------------------------------------------------------
@@ -1460,78 +1168,77 @@ cCiEnquiry::~cCiEnquiry()
free(text);
}
-bool cCiEnquiry::Reply(const char *s)
+void cCiEnquiry::Reply(const char *s)
{
cMutexLock MutexLock(mutex);
- return mmi ? mmi->SendAnswer(s) : false;
+ if (mmi)
+ mmi->SendAnswer(s);
}
-bool cCiEnquiry::Cancel(void)
+void cCiEnquiry::Cancel(void)
{
- return Reply(NULL);
+ Reply(NULL);
}
-bool cCiEnquiry::Abort(void)
+void cCiEnquiry::Abort(void)
{
cMutexLock MutexLock(mutex);
- return mmi && mmi->SendCloseMMI();
+ if (mmi)
+ mmi->SendCloseMMI();
}
-// -- cCiHandler -------------------------------------------------------------
+// --- cCiTransportConnection (cont'd) ---------------------------------------
-cCiHandler::cCiHandler(int Fd, int NumSlots)
+#define TC_POLL_TIMEOUT 300 // ms WORKAROUND: TC_POLL_TIMEOUT < 300ms doesn't work with DragonCAM
+#define TC_ALIVE_TIMEOUT 2000 // ms after which a transport connection is assumed dead
+
+cCiTransportConnection::cCiTransportConnection(cCamSlot *CamSlot, uint8_t Tcid)
{
- fd = Fd;
- numSlots = NumSlots;
- newCaSupport = false;
+ dbgprotocol("Slot %d: creating connection %d/%d\n", CamSlot->SlotNumber(), CamSlot->SlotIndex(), Tcid);
+ camSlot = CamSlot;
+ tcid = Tcid;
+ state = stIDLE;
+ createConnectionRequested = false;
+ deleteConnectionRequested = false;
hasUserIO = false;
- for (int i = 0; i < MAX_CI_SESSION; i++)
+ alive.Set(TC_ALIVE_TIMEOUT);
+ for (int i = 0; i <= MAX_SESSIONS_PER_TC; i++) // sessions[0] is not used, but initialized anyway
sessions[i] = NULL;
- for (int i = 0; i < MAX_CI_SLOT; i++)
- moduleReady[i] = false;
- tpl = new cCiTransportLayer(Fd, numSlots);
- tc = NULL;
- source = transponder = 0;
}
-cCiHandler::~cCiHandler()
+cCiTransportConnection::~cCiTransportConnection()
{
- for (int i = 0; i < MAX_CI_SESSION; i++)
+ for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++)
delete sessions[i];
- delete tpl;
- close(fd);
}
-cCiHandler *cCiHandler::CreateCiHandler(const char *FileName)
+bool cCiTransportConnection::Ready(void)
{
- int fd_ca = open(FileName, O_RDWR);
- if (fd_ca >= 0) {
- ca_caps_t Caps;
- if (ioctl(fd_ca, CA_GET_CAP, &Caps) == 0) {
- int NumSlots = Caps.slot_num;
- if (NumSlots > 0) {
- //XXX dsyslog("CAM: found %d CAM slots", NumSlots); // TODO let's do this only once we can be sure that there _really_ is a CAM adapter!
- if ((Caps.slot_type & CA_CI_LINK) != 0)
- return new cCiHandler(fd_ca, NumSlots);
- else
- isyslog("CAM doesn't support link layer interface");
- }
- else
- esyslog("ERROR: no CAM slots found");
- }
- else
- LOG_ERROR_STR(FileName);
- close(fd_ca);
- }
- return NULL;
+ cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
+ return cas && cas->Ready();
}
-uint32_t cCiHandler::ResourceIdToInt(const uint8_t *Data)
+const char *cCiTransportConnection::GetCamName(void)
{
- return (ntohl(get_unaligned((uint32_t *)Data)));
+ cCiApplicationInformation *ai = (cCiApplicationInformation *)GetSessionByResourceId(RI_APPLICATION_INFORMATION);
+ return ai ? ai->GetMenuString() : NULL;
}
-bool cCiHandler::Send(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId, int Status)
+void cCiTransportConnection::SendTPDU(uint8_t Tag, int Length, const uint8_t *Data)
+{
+ cTPDU TPDU(camSlot->SlotIndex(), tcid, Tag, Length, Data);
+ camSlot->Write(&TPDU);
+ timer.Set(TC_POLL_TIMEOUT);
+}
+
+void cCiTransportConnection::SendData(int Length, const uint8_t *Data)
+{
+ // if Length ever exceeds MAX_TPDU_DATA this needs to be handled differently
+ if (state == stACTIVE && Length > 0)
+ SendTPDU(T_DATA_LAST, Length, Data);
+}
+
+void cCiTransportConnection::SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId, int Status)
{
uint8_t buffer[16];
uint8_t *p = buffer;
@@ -1546,297 +1253,587 @@ bool cCiHandler::Send(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId, int
put_unaligned(htons(SessionId), (uint16_t *)p);
p += 2;
buffer[1] = p - buffer - 2; // length
- return tc && tc->SendData(p - buffer, buffer) == OK;
+ SendData(p - buffer, buffer);
}
-cCiSession *cCiHandler::GetSessionBySessionId(uint16_t SessionId)
+void cCiTransportConnection::Poll(void)
{
- for (int i = 0; i < MAX_CI_SESSION; i++) {
- if (sessions[i] && sessions[i]->SessionId() == SessionId)
- return sessions[i];
- }
- return NULL;
+ bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer;
+ DumpTPDUDataTransfer &= DumpPolls;
+ if (DumpPolls)
+ dbgprotocol("Slot %d: ==> Poll\n", camSlot->SlotNumber());
+ SendTPDU(T_DATA_LAST);
+ DumpTPDUDataTransfer = OldDumpTPDUDataTransfer;
}
-cCiSession *cCiHandler::GetSessionByResourceId(uint32_t ResourceId, int Slot)
+uint32_t cCiTransportConnection::ResourceIdToInt(const uint8_t *Data)
{
- for (int i = 0; i < MAX_CI_SESSION; i++) {
- if (sessions[i] && sessions[i]->Tc()->Slot() == Slot && sessions[i]->ResourceId() == ResourceId)
- return sessions[i];
- }
- return NULL;
+ return (ntohl(get_unaligned((uint32_t *)Data)));
}
-cCiSession *cCiHandler::CreateSession(uint32_t ResourceId)
+cCiSession *cCiTransportConnection::GetSessionBySessionId(uint16_t SessionId)
{
- if (!GetSessionByResourceId(ResourceId, tc->Slot())) {
- for (int i = 0; i < MAX_CI_SESSION; i++) {
- if (!sessions[i]) {
- switch (ResourceId) {
- case RI_RESOURCE_MANAGER: return sessions[i] = new cCiResourceManager(i + 1, tc);
- case RI_APPLICATION_INFORMATION: return sessions[i] = new cCiApplicationInformation(i + 1, tc);
- case RI_CONDITIONAL_ACCESS_SUPPORT: newCaSupport = true;
- return sessions[i] = new cCiConditionalAccessSupport(i + 1, tc);
- case RI_HOST_CONTROL: break; //XXX
- case RI_DATE_TIME: return sessions[i] = new cCiDateTime(i + 1, tc);
- case RI_MMI: return sessions[i] = new cCiMMI(i + 1, tc);
- }
- }
- }
- }
+ return (SessionId <= MAX_SESSIONS_PER_TC) ? sessions[SessionId] : NULL;
+}
+
+cCiSession *cCiTransportConnection::GetSessionByResourceId(uint32_t ResourceId)
+{
+ for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
+ if (sessions[i] && sessions[i]->ResourceId() == ResourceId)
+ return sessions[i];
+ }
return NULL;
}
-bool cCiHandler::OpenSession(int Length, const uint8_t *Data)
+void cCiTransportConnection::OpenSession(int Length, const uint8_t *Data)
{
if (Length == 6 && *(Data + 1) == 0x04) {
uint32_t ResourceId = ResourceIdToInt(Data + 2);
- dbgprotocol("OpenSession %08X\n", ResourceId);
- switch (ResourceId) {
- case RI_RESOURCE_MANAGER:
- case RI_APPLICATION_INFORMATION:
- case RI_CONDITIONAL_ACCESS_SUPPORT:
- case RI_HOST_CONTROL:
- case RI_DATE_TIME:
- case RI_MMI:
- {
- cCiSession *Session = CreateSession(ResourceId);
- if (Session) {
- Send(ST_OPEN_SESSION_RESPONSE, Session->SessionId(), Session->ResourceId(), SS_OK);
- return true;
+ dbgprotocol("Slot %d: open session %08X\n", camSlot->SlotNumber(), ResourceId);
+ if (!GetSessionByResourceId(ResourceId)) {
+ for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
+ if (!sessions[i]) {
+ switch (ResourceId) {
+ case RI_RESOURCE_MANAGER: sessions[i] = new cCiResourceManager(i, this); break;
+ case RI_APPLICATION_INFORMATION: sessions[i] = new cCiApplicationInformation(i, this); break;
+ case RI_CONDITIONAL_ACCESS_SUPPORT: sessions[i] = new cCiConditionalAccessSupport(i, this); break;
+ case RI_DATE_TIME: sessions[i] = new cCiDateTime(i, this); break;
+ case RI_MMI: sessions[i] = new cCiMMI(i, this); break;
+ case RI_HOST_CONTROL: // not implemented
+ default: esyslog("ERROR: CAM %d: unknown resource identifier: %08X (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
}
- esyslog("ERROR: can't create session for resource identifier: %08X", ResourceId);
+ if (sessions[i])
+ SendTag(ST_OPEN_SESSION_RESPONSE, sessions[i]->SessionId(), sessions[i]->ResourceId(), SS_OK);
+ return;
+ }
}
- break;
- default: esyslog("ERROR: unknown resource identifier: %08X", ResourceId);
- }
+ esyslog("ERROR: CAM %d: no free session slot for resource identifier %08X (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
+ }
+ else
+ esyslog("ERROR: CAM %d: session for resource identifier %08X already exists (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
}
- return false;
}
-bool cCiHandler::CloseSession(uint16_t SessionId)
+void cCiTransportConnection::CloseSession(uint16_t SessionId)
{
- dbgprotocol("CloseSession %d\n", SessionId);
+ dbgprotocol("Slot %d: close session %d\n", camSlot->SlotNumber(), SessionId);
cCiSession *Session = GetSessionBySessionId(SessionId);
- if (Session && sessions[SessionId - 1] == Session) {
+ if (Session && sessions[SessionId] == Session) {
delete Session;
- sessions[SessionId - 1] = NULL;
- Send(ST_CLOSE_SESSION_RESPONSE, SessionId, 0, SS_OK);
- return true;
+ sessions[SessionId] = NULL;
+ SendTag(ST_CLOSE_SESSION_RESPONSE, SessionId, 0, SS_OK);
}
else {
- esyslog("ERROR: unknown session id: %d", SessionId);
- Send(ST_CLOSE_SESSION_RESPONSE, SessionId, 0, SS_NOT_ALLOCATED);
+ esyslog("ERROR: CAM %d: unknown session id: %d (%d/%d)", camSlot->SlotNumber(), SessionId, camSlot->SlotIndex(), tcid);
+ SendTag(ST_CLOSE_SESSION_RESPONSE, SessionId, 0, SS_NOT_ALLOCATED);
+ }
+}
+
+void cCiTransportConnection::HandleSessions(cTPDU *TPDU)
+{
+ int Length;
+ const uint8_t *Data = TPDU->Data(Length);
+ if (Data && Length > 1) {
+ switch (*Data) {
+ case ST_SESSION_NUMBER: if (Length > 4) {
+ uint16_t SessionId = ntohs(get_unaligned((uint16_t *)&Data[2]));
+ cCiSession *Session = GetSessionBySessionId(SessionId);
+ if (Session)
+ Session->Process(Length - 4, Data + 4);
+ else
+ esyslog("ERROR: CAM %d: unknown session id: %d (%d/%d)", camSlot->SlotNumber(), SessionId, camSlot->SlotIndex(), tcid);
+ }
+ break;
+ case ST_OPEN_SESSION_REQUEST: OpenSession(Length, Data);
+ break;
+ case ST_CLOSE_SESSION_REQUEST: if (Length == 4)
+ CloseSession(ntohs(get_unaligned((uint16_t *)&Data[2])));
+ break;
+ case ST_CREATE_SESSION_RESPONSE: // not implemented
+ case ST_CLOSE_SESSION_RESPONSE: // not implemented
+ default: esyslog("ERROR: CAM %d: unknown session tag: %02X (%d/%d)", camSlot->SlotNumber(), *Data, camSlot->SlotIndex(), tcid);
+ }
}
- return false;
}
-int cCiHandler::CloseAllSessions(int Slot)
+bool cCiTransportConnection::Process(cTPDU *TPDU)
{
- int result = 0;
- for (int i = 0; i < MAX_CI_SESSION; i++) {
- if (sessions[i] && sessions[i]->Tc()->Slot() == Slot) {
- CloseSession(sessions[i]->SessionId());
- result++;
- }
- }
- return result;
+ if (TPDU)
+ alive.Set(TC_ALIVE_TIMEOUT);
+ else if (alive.TimedOut())
+ return false;
+ switch (state) {
+ case stIDLE:
+ if (createConnectionRequested) {
+ dbgprotocol("Slot %d: create connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
+ createConnectionRequested = false;
+ SendTPDU(T_CREATE_TC);
+ state = stCREATION;
+ }
+ return true;
+ case stCREATION:
+ if (TPDU && TPDU->Tag() == T_CTC_REPLY) {
+ dbgprotocol("Slot %d: connection created %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
+ Poll();
+ state = stACTIVE;
+ }
+ else if (timer.TimedOut()) {
+ dbgprotocol("Slot %d: timeout while creating connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
+ state = stIDLE;
+ }
+ return true;
+ case stACTIVE:
+ if (deleteConnectionRequested) {
+ dbgprotocol("Slot %d: delete connection requested %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
+ deleteConnectionRequested = false;
+ SendTPDU(T_DELETE_TC);
+ state = stDELETION;
+ return true;
+ }
+ if (TPDU) {
+ switch (TPDU->Tag()) {
+ case T_REQUEST_TC:
+ esyslog("ERROR: CAM %d: T_REQUEST_TC not implemented (%d/%d)", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
+ break;
+ case T_DATA_MORE:
+ case T_DATA_LAST:
+ HandleSessions(TPDU);
+ // continue with T_SB
+ case T_SB:
+ if ((TPDU->Status() & DATA_INDICATOR) != 0) {
+ dbgprotocol("Slot %d: receive data %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
+ SendTPDU(T_RCV);
+ }
+ break;
+ case T_DELETE_TC:
+ dbgprotocol("Slot %d: delete connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
+ SendTPDU(T_DTC_REPLY);
+ state = stIDLE;
+ return true;
+ }
+ }
+ else if (timer.TimedOut())
+ Poll();
+ hasUserIO = false;
+ for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
+ if (sessions[i]) {
+ sessions[i]->Process();
+ if (sessions[i]->HasUserIO())
+ hasUserIO = true;
+ }
+ }
+ break;
+ case stDELETION:
+ if (TPDU && TPDU->Tag() == T_DTC_REPLY || timer.TimedOut()) {
+ dbgprotocol("Slot %d: connection deleted %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
+ state = stIDLE;
+ }
+ return true;
+ }
+ return true;
}
-int cCiHandler::NumCams(void)
+// --- cCiCaPidData ----------------------------------------------------------
+
+class cCiCaPidData : public cListObject {
+public:
+ bool active;
+ int pid;
+ int streamType;
+ cCiCaPidData(int Pid, int StreamType)
+ {
+ active = false;
+ pid = Pid;
+ streamType = StreamType;
+ }
+ };
+
+// --- cCiCaProgramData ------------------------------------------------------
+
+class cCiCaProgramData : public cListObject {
+public:
+ int programNumber;
+ bool modified;
+ cList<cCiCaPidData> pidList;
+ cCiCaProgramData(int ProgramNumber)
+ {
+ programNumber = ProgramNumber;
+ modified = false;
+ }
+ };
+
+// --- cCiAdapter ------------------------------------------------------------
+
+cCiAdapter::cCiAdapter(void)
+:cThread("CI adapter")
{
- int result = 0;
- for (int i = 0; i < MAX_CI_SLOT; i++)
- if (moduleReady[i])
- result++;
- return result;
+ assignedDevice = NULL;
+ for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++)
+ camSlots[i] = NULL;
}
-bool cCiHandler::Ready(void)
+cCiAdapter::~cCiAdapter()
{
- cMutexLock MutexLock(&mutex);
- for (int Slot = 0; Slot < numSlots; Slot++) {
- if (moduleReady[Slot]) {
- cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT, Slot);
- if (!cas || !*cas->GetCaSystemIds())
- return false;
- }
+ Cancel(3);
+ for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++)
+ delete camSlots[i];
+}
+
+void cCiAdapter::AddCamSlot(cCamSlot *CamSlot)
+{
+ if (CamSlot) {
+ for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) {
+ if (!camSlots[i]) {
+ CamSlot->slotIndex = i;
+ camSlots[i] = CamSlot;
+ return;
+ }
+ }
+ esyslog("ERROR: no free CAM slot in CI adapter");
+ }
+}
+
+bool cCiAdapter::Ready(void)
+{
+ for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) {
+ if (camSlots[i] && !camSlots[i]->Ready())
+ return false;
}
return true;
}
-bool cCiHandler::Process(int Slot)
+void cCiAdapter::Action(void)
+{
+ cTPDU TPDU;
+ while (Running()) {
+ int n = Read(TPDU.Buffer(), TPDU.MaxSize());
+ if (n > 0 && TPDU.Slot() < MAX_CAM_SLOTS_PER_ADAPTER) {
+ TPDU.SetSize(n);
+ cCamSlot *cs = camSlots[TPDU.Slot()];
+ TPDU.Dump(cs ? cs->SlotNumber() : 0, false);
+ if (cs)
+ cs->Process(&TPDU);
+ }
+ for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) {
+ if (camSlots[i])
+ camSlots[i]->Process();
+ }
+ }
+}
+
+// --- cCamSlot --------------------------------------------------------------
+
+cCamSlots CamSlots;
+
+#define MODULE_CHECK_INTERVAL 100 // ms
+#define MODULE_RESET_TIMEOUT 2 // s
+
+cCamSlot::cCamSlot(cCiAdapter *CiAdapter)
+{
+ ciAdapter = CiAdapter;
+ slotIndex = -1;
+ lastModuleStatus = msReset; // avoids initial reset log message
+ resetTime = 0;
+ resendPmt = false;
+ source = transponder = 0;
+ for (int i = 0; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) // tc[0] is not used, but initialized anyway
+ tc[i] = NULL;
+ CamSlots.Add(this);
+ slotNumber = Index() + 1;
+ if (ciAdapter)
+ ciAdapter->AddCamSlot(this);
+ Reset();
+}
+
+cCamSlot::~cCamSlot()
+{
+ CamSlots.Del(this, false);
+ DeleteAllConnections();
+}
+
+bool cCamSlot::Assign(cDevice *Device, bool Query)
{
- bool result = true;
cMutexLock MutexLock(&mutex);
- for (int slot = 0; slot < numSlots; slot++) {
- if (Slot < 0 || slot == Slot) {
- tc = tpl->Process(slot);
- if (tc) {
- int Length;
- const uint8_t *Data = tc->Data(Length);
- if (Data && Length > 1) {
- switch (*Data) {
- case ST_SESSION_NUMBER: if (Length > 4) {
- uint16_t SessionId = ntohs(get_unaligned((uint16_t *)&Data[2]));
- cCiSession *Session = GetSessionBySessionId(SessionId);
- if (Session)
- Session->Process(Length - 4, Data + 4);
- else
- esyslog("ERROR: unknown session id: %d", SessionId);
- }
- break;
- case ST_OPEN_SESSION_REQUEST: OpenSession(Length, Data);
- break;
- case ST_CLOSE_SESSION_REQUEST: if (Length == 4)
- CloseSession(ntohs(get_unaligned((uint16_t *)&Data[2])));
- break;
- case ST_CREATE_SESSION_RESPONSE: //XXX fall through to default
- case ST_CLOSE_SESSION_RESPONSE: //XXX fall through to default
- default: esyslog("ERROR: unknown session tag: %02X", *Data);
+ if (ciAdapter) {
+ if (ciAdapter->Assign(Device, true)) {
+ if (!Device && ciAdapter->assignedDevice)
+ ciAdapter->assignedDevice->SetCamSlot(NULL);
+ if (!Query) {
+ StopDecrypting();
+ source = transponder = 0;
+ if (ciAdapter->Assign(Device)) {
+ ciAdapter->assignedDevice = Device;
+ if (Device) {
+ Device->SetCamSlot(this);
+ dsyslog("CAM %d: assigned to device %d", slotNumber, Device->DeviceNumber() + 1);
}
- }
- }
- else if (CloseAllSessions(slot)) {
- tpl->ResetSlot(slot);
- result = false;
- }
- else if (tpl->ModuleReady(slot)) {
- dbgprotocol("Module ready in slot %d\n", slot);
- moduleReady[slot] = true;
- tpl->NewConnection(slot);
- }
+ else
+ dsyslog("CAM %d: unassigned", slotNumber);
+ }
+ else
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+cDevice *cCamSlot::Device(void)
+{
+ cMutexLock MutexLock(&mutex);
+ if (ciAdapter) {
+ cDevice *d = ciAdapter->assignedDevice;
+ if (d && d->CamSlot() == this)
+ return d;
+ }
+ return NULL;
+}
+
+void cCamSlot::NewConnection(void)
+{
+ cMutexLock MutexLock(&mutex);
+ for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) {
+ if (!tc[i]) {
+ tc[i] = new cCiTransportConnection(this, i);
+ tc[i]->CreateConnection();
+ return;
}
}
- SendCaPmt();
- bool UserIO = false;
- for (int i = 0; i < MAX_CI_SESSION; i++) {
- if (sessions[i] && sessions[i]->Process())
- UserIO |= sessions[i]->HasUserIO();
+ esyslog("ERROR: CAM %d: can't create new transport connection!", slotNumber);
+}
+
+void cCamSlot::DeleteAllConnections(void)
+{
+ cMutexLock MutexLock(&mutex);
+ for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) {
+ delete tc[i];
+ tc[i] = NULL;
}
- hasUserIO = UserIO;
- return result;
}
-void cCiHandler::SendCaPmt(void)
+void cCamSlot::Process(cTPDU *TPDU)
{
cMutexLock MutexLock(&mutex);
- if (newCaSupport) {
- newCaSupport = false;
- for (int Slot = 0; Slot < numSlots; Slot++) {
- cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT, Slot);
- if (cas) {
- // build the list of CA_PMT data:
- cList<cCiCaPmt> CaPmtList;
- for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
- bool Active = false;
- cCiCaPmt *CaPmt = new cCiCaPmt(CPCI_OK_DESCRAMBLING, source, transponder, p->programNumber, GetCaSystemIds(Slot));
- if (CaPmt->Valid()) {
- for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
- if (q->active) {
- CaPmt->AddPid(q->pid, q->streamType);
- Active = true;
- }
- }
- }
- if (Active)
- CaPmtList.Add(CaPmt);
- else
- delete CaPmt;
- }
- // send the CA_PMT data:
- uint8_t ListManagement = CaPmtList.Count() > 1 ? CPLM_FIRST : CPLM_ONLY;
- for (cCiCaPmt *CaPmt = CaPmtList.First(); CaPmt; CaPmt = CaPmtList.Next(CaPmt)) {
- CaPmt->SetListManagement(ListManagement);
- if (!cas->SendPMT(CaPmt))
- newCaSupport = true;
- ListManagement = CaPmt->Next() && CaPmt->Next()->Next() ? CPLM_MORE : CPLM_LAST;
- }
- }
+ if (TPDU) {
+ int n = TPDU->Tcid();
+ if (1 <= n && n <= MAX_CONNECTIONS_PER_CAM_SLOT) {
+ if (tc[n])
+ tc[n]->Process(TPDU);
+ }
+ }
+ for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) {
+ if (tc[i]) {
+ if (!tc[i]->Process()) {
+ Reset();
+ return;
+ }
}
+ }
+ if (moduleCheckTimer.TimedOut()) {
+ eModuleStatus ms = ModuleStatus();
+ if (ms != lastModuleStatus) {
+ switch (ms) {
+ case msNone:
+ dbgprotocol("Slot %d: no module present\n", slotNumber);
+ isyslog("CAM %d: no module present", slotNumber);
+ DeleteAllConnections();
+ break;
+ case msReset:
+ dbgprotocol("Slot %d: module reset\n", slotNumber);
+ isyslog("CAM %d: module reset", slotNumber);
+ DeleteAllConnections();
+ break;
+ case msPresent:
+ dbgprotocol("Slot %d: module present\n", slotNumber);
+ isyslog("CAM %d: module present", slotNumber);
+ break;
+ case msReady:
+ dbgprotocol("Slot %d: module ready\n", slotNumber);
+ isyslog("CAM %d: module ready", slotNumber);
+ NewConnection();
+ resendPmt = caProgramList.Count() > 0;
+ break;
+ }
+ lastModuleStatus = ms;
+ }
+ moduleCheckTimer.Set(MODULE_CHECK_INTERVAL);
+ }
+ if (resendPmt)
+ SendCaPmt(CPCI_OK_DESCRAMBLING);
+ processed.Broadcast();
+}
+
+cCiSession *cCamSlot::GetSessionByResourceId(uint32_t ResourceId)
+{
+ cMutexLock MutexLock(&mutex);
+ return tc[1] ? tc[1]->GetSessionByResourceId(ResourceId) : NULL;
+}
+
+void cCamSlot::Write(cTPDU *TPDU)
+{
+ cMutexLock MutexLock(&mutex);
+ if (ciAdapter && TPDU->Size()) {
+ TPDU->Dump(SlotNumber(), true);
+ ciAdapter->Write(TPDU->Buffer(), TPDU->Size());
+ }
+}
+
+bool cCamSlot::Reset(void)
+{
+ cMutexLock MutexLock(&mutex);
+ ChannelCamRelations.Reset(slotNumber);
+ DeleteAllConnections();
+ if (ciAdapter) {
+ dbgprotocol("Slot %d: reset...", slotNumber);
+ if (ciAdapter->Reset(slotIndex)) {
+ resetTime = time(NULL);
+ dbgprotocol("ok.\n");
+ return true;
+ }
+ dbgprotocol("failed!\n");
+ }
+ return false;
+}
+
+eModuleStatus cCamSlot::ModuleStatus(void)
+{
+ cMutexLock MutexLock(&mutex);
+ eModuleStatus ms = ciAdapter ? ciAdapter->ModuleStatus(slotIndex) : msNone;
+ if (resetTime) {
+ if (ms <= msReset) {
+ if (time(NULL) - resetTime < MODULE_RESET_TIMEOUT)
+ return msReset;
+ }
+ resetTime = 0;
}
+ return ms;
+}
+
+const char *cCamSlot::GetCamName(void)
+{
+ cMutexLock MutexLock(&mutex);
+ return tc[1] ? tc[1]->GetCamName() : NULL;
}
-bool cCiHandler::EnterMenu(int Slot)
+bool cCamSlot::Ready(void)
{
cMutexLock MutexLock(&mutex);
- cCiApplicationInformation *api = (cCiApplicationInformation *)GetSessionByResourceId(RI_APPLICATION_INFORMATION, Slot);
+ return ModuleStatus() == msNone || tc[1] && tc[1]->Ready();
+}
+
+bool cCamSlot::HasMMI(void)
+{
+ return GetSessionByResourceId(RI_MMI);
+}
+
+bool cCamSlot::HasUserIO(void)
+{
+ cMutexLock MutexLock(&mutex);
+ return tc[1] && tc[1]->HasUserIO();
+}
+
+bool cCamSlot::EnterMenu(void)
+{
+ cMutexLock MutexLock(&mutex);
+ cCiApplicationInformation *api = (cCiApplicationInformation *)GetSessionByResourceId(RI_APPLICATION_INFORMATION);
return api ? api->EnterMenu() : false;
}
-cCiMenu *cCiHandler::GetMenu(void)
+cCiMenu *cCamSlot::GetMenu(void)
{
cMutexLock MutexLock(&mutex);
- for (int Slot = 0; Slot < numSlots; Slot++) {
- cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI, Slot);
- if (mmi) {
- cCiMenu *Menu = mmi->Menu();
- if (Menu)
- Menu->mutex = &mutex;
- return Menu;
- }
- }
+ cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI);
+ if (mmi) {
+ cCiMenu *Menu = mmi->Menu();
+ if (Menu)
+ Menu->mutex = &mutex;
+ return Menu;
+ }
return NULL;
}
-cCiEnquiry *cCiHandler::GetEnquiry(void)
+cCiEnquiry *cCamSlot::GetEnquiry(void)
{
cMutexLock MutexLock(&mutex);
- for (int Slot = 0; Slot < numSlots; Slot++) {
- cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI, Slot);
- if (mmi) {
- cCiEnquiry *Enquiry = mmi->Enquiry();
- if (Enquiry)
- Enquiry->mutex = &mutex;
- return Enquiry;
- }
- }
+ cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI);
+ if (mmi) {
+ cCiEnquiry *Enquiry = mmi->Enquiry();
+ if (Enquiry)
+ Enquiry->mutex = &mutex;
+ return Enquiry;
+ }
return NULL;
}
-const char *cCiHandler::GetCamName(int Slot)
+void cCamSlot::SendCaPmt(uint8_t CmdId)
{
cMutexLock MutexLock(&mutex);
- cCiApplicationInformation *ai = (cCiApplicationInformation *)GetSessionByResourceId(RI_APPLICATION_INFORMATION, Slot);
- return ai ? ai->GetMenuString() : NULL;
+ cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
+ if (cas) {
+ const int *CaSystemIds = cas->GetCaSystemIds();
+ if (CaSystemIds && *CaSystemIds) {
+ if (caProgramList.Count()) {
+ for (int Loop = 1; Loop <= 2; Loop++) {
+ for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
+ if (p->modified || resendPmt) {
+ bool Active = false;
+ cCiCaPmt CaPmt(CmdId, source, transponder, p->programNumber, CaSystemIds);
+ for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
+ if (q->active) {
+ CaPmt.AddPid(q->pid, q->streamType);
+ Active = true;
+ }
+ }
+ if ((Loop == 1) != Active) { // first remove, then add
+ if (cas->RepliesToQuery())
+ CaPmt.SetListManagement(Active ? CPLM_ADD : CPLM_UPDATE);
+ if (Active || cas->RepliesToQuery())
+ cas->SendPMT(&CaPmt);
+ p->modified = false;
+ }
+ }
+ }
+ }
+ resendPmt = false;
+ }
+ else {
+ cCiCaPmt CaPmt(CmdId, 0, 0, 0, NULL);
+ cas->SendPMT(&CaPmt);
+ }
+ }
+ }
}
-const unsigned short *cCiHandler::GetCaSystemIds(int Slot)
+const int *cCamSlot::GetCaSystemIds(void)
{
cMutexLock MutexLock(&mutex);
- cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT, Slot);
+ cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
return cas ? cas->GetCaSystemIds() : NULL;
}
-bool cCiHandler::ProvidesCa(const unsigned short *CaSystemIds)
+int cCamSlot::Priority(void)
{
- cMutexLock MutexLock(&mutex);
- for (int Slot = 0; Slot < numSlots; Slot++) {
- cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT, Slot);
- if (cas) {
- for (const unsigned short *ids = cas->GetCaSystemIds(); ids && *ids; ids++) {
- for (const unsigned short *id = CaSystemIds; *id; id++) {
- if (*id == *ids)
- return true;
- }
- }
- }
- }
- return false;
+ cDevice *d = Device();
+ return d ? d->Priority() : -1;
}
-void cCiHandler::SetSource(int Source, int Transponder)
+bool cCamSlot::ProvidesCa(const int *CaSystemIds)
{
cMutexLock MutexLock(&mutex);
- if (source != Source || transponder != Transponder) {
- //XXX if there are active entries, send an empty CA_PMT
- caProgramList.Clear();
+ cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
+ if (cas) {
+ for (const int *ids = cas->GetCaSystemIds(); ids && *ids; ids++) {
+ for (const int *id = CaSystemIds; *id; id++) {
+ if (*id == *ids)
+ return true;
+ }
+ }
}
- source = Source;
- transponder = Transponder;
+ return false;
}
-void cCiHandler::AddPid(int ProgramNumber, int Pid, int StreamType)
+void cCamSlot::AddPid(int ProgramNumber, int Pid, int StreamType)
{
cMutexLock MutexLock(&mutex);
cCiCaProgramData *ProgramData = NULL;
@@ -1854,65 +1851,276 @@ void cCiHandler::AddPid(int ProgramNumber, int Pid, int StreamType)
ProgramData->pidList.Add(new cCiCaPidData(Pid, StreamType));
}
-void cCiHandler::SetPid(int Pid, bool Active)
+void cCamSlot::SetPid(int Pid, bool Active)
{
cMutexLock MutexLock(&mutex);
for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
if (q->pid == Pid) {
- q->active = Active;
+ if (q->active != Active) {
+ q->active = Active;
+ p->modified = true;
+ }
return;
}
}
}
}
-bool cCiHandler::CanDecrypt(int ProgramNumber)
+// see ISO/IEC 13818-1
+#define STREAM_TYPE_VIDEO 0x02
+#define STREAM_TYPE_AUDIO 0x04
+#define STREAM_TYPE_DOLBY 0x06
+
+void cCamSlot::AddChannel(const cChannel *Channel)
{
cMutexLock MutexLock(&mutex);
- for (int Slot = 0; Slot < numSlots; Slot++) {
- cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT, Slot);
- if (cas) {
- for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
- if (p->programNumber == ProgramNumber) {
- cCiCaPmt CaPmt(CPCI_QUERY, source, transponder, p->programNumber, GetCaSystemIds(Slot));//XXX???
- if (CaPmt.Valid()) {
- for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
-//XXX if (q->active)
- CaPmt.AddPid(q->pid, q->streamType);
- }
- }
- if (!cas->SendPMT(&CaPmt))
- return false;//XXX
- //XXX
- time_t timeout = time(NULL) + 3;//XXX
- while (time(NULL) <= timeout) {
- Process(Slot);
- cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT, Slot);
- if (!cas)
- return false;//XXX
- if (cas->ReceivedReply(true))
- return true;
- //XXX remember if a slot doesn't receive a reply
- }
- break;
- }
+ if (source != Channel->Source() || transponder != Channel->Transponder())
+ StopDecrypting();
+ source = Channel->Source();
+ transponder = Channel->Transponder();
+ if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
+ AddPid(Channel->Sid(), Channel->Vpid(), STREAM_TYPE_VIDEO);
+ for (const int *Apid = Channel->Apids(); *Apid; Apid++)
+ AddPid(Channel->Sid(), *Apid, STREAM_TYPE_AUDIO);
+ for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
+ AddPid(Channel->Sid(), *Dpid, STREAM_TYPE_DOLBY);
+ }
+}
+
+#define QUERY_REPLY_WAIT 100 // ms to wait between checks for a reply
+
+bool cCamSlot::CanDecrypt(const cChannel *Channel)
+{
+ if (Channel->Ca() < CA_ENCRYPTED_MIN)
+ return true; // channel not encrypted
+ if (!IsDecrypting())
+ return true; // any CAM can decrypt at least one channel
+ cMutexLock MutexLock(&mutex);
+ cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
+ if (cas && cas->RepliesToQuery()) {
+ cCiCaPmt CaPmt(CPCI_QUERY, Channel->Source(), Channel->Transponder(), Channel->Sid(), GetCaSystemIds());
+ CaPmt.SetListManagement(CPLM_ADD); // WORKAROUND: CPLM_ONLY doesn't work with Alphacrypt 3.09 (deletes existing CA_PMTs)
+ CaPmt.AddPid(Channel->Vpid(), STREAM_TYPE_VIDEO);
+ for (const int *Apid = Channel->Apids(); *Apid; Apid++)
+ CaPmt.AddPid(*Apid, STREAM_TYPE_AUDIO);
+ for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
+ CaPmt.AddPid(*Dpid, STREAM_TYPE_DOLBY);
+ cas->SendPMT(&CaPmt);
+ cTimeMs Timeout(QUERY_REPLY_TIMEOUT);
+ do {
+ processed.TimedWait(mutex, QUERY_REPLY_WAIT);
+ if ((cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT)) != NULL) { // must re-fetch it, there might have been a reset
+ if (cas->ReceivedReply())
+ return cas->CanDecrypt();
+ }
+ else
+ return false;
+ } while (!Timeout.TimedOut());
+ dsyslog("CAM %d: didn't reply to QUERY", SlotNumber());
+ }
+ return false;
+}
+
+void cCamSlot::StartDecrypting(void)
+{
+ SendCaPmt(CPCI_OK_DESCRAMBLING);
+}
+
+void cCamSlot::StopDecrypting(void)
+{
+ cMutexLock MutexLock(&mutex);
+ if (caProgramList.Count()) {
+ caProgramList.Clear();
+ SendCaPmt(CPCI_NOT_SELECTED);
+ }
+}
+
+bool cCamSlot::IsDecrypting(void)
+{
+ cMutexLock MutexLock(&mutex);
+ if (caProgramList.Count()) {
+ for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
+ if (p->modified)
+ return true; // any modifications need to be processed before we can assume it's no longer decrypting
+ for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
+ if (q->active)
+ return true;
}
}
- }
+ }
return false;
}
-void cCiHandler::StartDecrypting(void)
+// --- cChannelCamRelation ---------------------------------------------------
+
+#define CAM_CHECKED_TIMEOUT 15 // seconds before a CAM that has been checked for a particular channel will be checked again
+
+class cChannelCamRelation : public cListObject {
+private:
+ tChannelID channelID;
+ uint32_t camSlotsChecked;
+ uint32_t camSlotsDecrypt;
+ time_t lastChecked;
+public:
+ cChannelCamRelation(tChannelID ChannelID);
+ bool TimedOut(void);
+ tChannelID ChannelID(void) { return channelID; }
+ bool CamChecked(int CamSlotNumber);
+ bool CamDecrypt(int CamSlotNumber);
+ void SetChecked(int CamSlotNumber);
+ void SetDecrypt(int CamSlotNumber);
+ void ClrChecked(int CamSlotNumber);
+ void ClrDecrypt(int CamSlotNumber);
+ };
+
+cChannelCamRelation::cChannelCamRelation(tChannelID ChannelID)
+{
+ channelID = ChannelID;
+ camSlotsChecked = 0;
+ camSlotsDecrypt = 0;
+ lastChecked = 0;
+}
+
+bool cChannelCamRelation::TimedOut(void)
+{
+ return !camSlotsDecrypt && time(NULL) - lastChecked > CAM_CHECKED_TIMEOUT;
+}
+
+bool cChannelCamRelation::CamChecked(int CamSlotNumber)
+{
+ if (lastChecked && time(NULL) - lastChecked > CAM_CHECKED_TIMEOUT) {
+ lastChecked = 0;
+ camSlotsChecked = 0;
+ }
+ return camSlotsChecked & (1 << (CamSlotNumber - 1));
+}
+
+bool cChannelCamRelation::CamDecrypt(int CamSlotNumber)
+{
+ return camSlotsDecrypt & (1 << (CamSlotNumber - 1));
+}
+
+void cChannelCamRelation::SetChecked(int CamSlotNumber)
+{
+ camSlotsChecked |= (1 << (CamSlotNumber - 1));
+ lastChecked = time(NULL);
+ ClrDecrypt(CamSlotNumber);
+}
+
+void cChannelCamRelation::SetDecrypt(int CamSlotNumber)
+{
+ camSlotsDecrypt |= (1 << (CamSlotNumber - 1));
+ ClrChecked(CamSlotNumber);
+}
+
+void cChannelCamRelation::ClrChecked(int CamSlotNumber)
+{
+ camSlotsChecked &= ~(1 << (CamSlotNumber - 1));
+ lastChecked = 0;
+}
+
+void cChannelCamRelation::ClrDecrypt(int CamSlotNumber)
+{
+ camSlotsDecrypt &= ~(1 << (CamSlotNumber - 1));
+}
+
+// --- cChannelCamRelations --------------------------------------------------
+
+#define CHANNEL_CAM_RELATIONS_CLEANUP_INTERVAL 3600 // seconds between cleanups
+
+cChannelCamRelations ChannelCamRelations;
+
+cChannelCamRelations::cChannelCamRelations(void)
+{
+ lastCleanup = time(NULL);
+}
+
+void cChannelCamRelations::Cleanup(void)
+{
+ cMutexLock MutexLock(&mutex);
+ if (time(NULL) - lastCleanup > CHANNEL_CAM_RELATIONS_CLEANUP_INTERVAL) {
+ for (cChannelCamRelation *ccr = First(); ccr; ) {
+ cChannelCamRelation *c = ccr;
+ ccr = Next(ccr);
+ if (c->TimedOut())
+ Del(c);
+ }
+ lastCleanup = time(NULL);
+ }
+}
+
+cChannelCamRelation *cChannelCamRelations::GetEntry(tChannelID ChannelID)
+{
+ cMutexLock MutexLock(&mutex);
+ Cleanup();
+ for (cChannelCamRelation *ccr = First(); ccr; ccr = Next(ccr)) {
+ if (ccr->ChannelID() == ChannelID)
+ return ccr;
+ }
+ return NULL;
+}
+
+cChannelCamRelation *cChannelCamRelations::AddEntry(tChannelID ChannelID)
+{
+ cMutexLock MutexLock(&mutex);
+ cChannelCamRelation *ccr = GetEntry(ChannelID);
+ if (!ccr)
+ Add(ccr = new cChannelCamRelation(ChannelID));
+ return ccr;
+}
+
+void cChannelCamRelations::Reset(int CamSlotNumber)
+{
+ cMutexLock MutexLock(&mutex);
+ for (cChannelCamRelation *ccr = First(); ccr; ccr = Next(ccr)) {
+ ccr->ClrChecked(CamSlotNumber);
+ ccr->ClrDecrypt(CamSlotNumber);
+ }
+}
+
+bool cChannelCamRelations::CamChecked(tChannelID ChannelID, int CamSlotNumber)
+{
+ cMutexLock MutexLock(&mutex);
+ cChannelCamRelation *ccr = GetEntry(ChannelID);
+ return ccr ? ccr->CamChecked(CamSlotNumber) : false;
+}
+
+bool cChannelCamRelations::CamDecrypt(tChannelID ChannelID, int CamSlotNumber)
+{
+ cMutexLock MutexLock(&mutex);
+ cChannelCamRelation *ccr = GetEntry(ChannelID);
+ return ccr ? ccr->CamDecrypt(CamSlotNumber) : false;
+}
+
+void cChannelCamRelations::SetChecked(tChannelID ChannelID, int CamSlotNumber)
+{
+ cMutexLock MutexLock(&mutex);
+ cChannelCamRelation *ccr = AddEntry(ChannelID);
+ if (ccr)
+ ccr->SetChecked(CamSlotNumber);
+}
+
+void cChannelCamRelations::SetDecrypt(tChannelID ChannelID, int CamSlotNumber)
+{
+ cMutexLock MutexLock(&mutex);
+ cChannelCamRelation *ccr = AddEntry(ChannelID);
+ if (ccr)
+ ccr->SetDecrypt(CamSlotNumber);
+}
+
+void cChannelCamRelations::ClrChecked(tChannelID ChannelID, int CamSlotNumber)
{
cMutexLock MutexLock(&mutex);
- newCaSupport = true;
- SendCaPmt();
+ cChannelCamRelation *ccr = GetEntry(ChannelID);
+ if (ccr)
+ ccr->ClrChecked(CamSlotNumber);
}
-bool cCiHandler::Reset(int Slot)
+void cChannelCamRelations::ClrDecrypt(tChannelID ChannelID, int CamSlotNumber)
{
cMutexLock MutexLock(&mutex);
- CloseAllSessions(Slot);
- return tpl->ResetSlot(Slot, true);
+ cChannelCamRelation *ccr = GetEntry(ChannelID);
+ if (ccr)
+ ccr->ClrDecrypt(CamSlotNumber);
}
diff --git a/ci.h b/ci.h
index 45740106..678f17ba 100644
--- a/ci.h
+++ b/ci.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: ci.h 1.22 2006/08/12 09:43:31 kls Exp $
+ * $Id: ci.h 1.23 2007/01/07 14:38:00 kls Exp $
*/
#ifndef __CI_H
@@ -12,13 +12,18 @@
#include <stdint.h>
#include <stdio.h>
+#include "channels.h"
#include "thread.h"
#include "tools.h"
+#define MAX_CAM_SLOTS_PER_ADAPTER 8 // maximum possible value is 255
+#define MAX_CONNECTIONS_PER_CAM_SLOT 8 // maximum possible value is 254
+#define CAM_READ_TIMEOUT 50 // ms
+
class cCiMMI;
class cCiMenu {
- friend class cCiHandler;
+ friend class cCamSlot;
friend class cCiMMI;
private:
enum { MAX_CIMENU_ENTRIES = 64 }; ///< XXX is there a specified maximum?
@@ -40,14 +45,14 @@ public:
const char *Entry(int n) { return n < numEntries ? entries[n] : NULL; }
int NumEntries(void) { return numEntries; }
bool Selectable(void) { return selectable; }
- bool Select(int Index);
- bool Cancel(void);
- bool Abort(void);
+ void Select(int Index);
+ void Cancel(void);
+ void Abort(void);
bool HasUpdate(void);
};
class cCiEnquiry {
- friend class cCiHandler;
+ friend class cCamSlot;
friend class cCiMMI;
private:
cCiMMI *mmi;
@@ -61,103 +66,140 @@ public:
const char *Text(void) { return text; }
bool Blind(void) { return blind; }
int ExpectedLength(void) { return expectedLength; }
- bool Reply(const char *s);
- bool Cancel(void);
- bool Abort(void);
+ void Reply(const char *s);
+ void Cancel(void);
+ void Abort(void);
};
-#define MAX_CI_SESSION 16 //XXX
-#define MAX_CI_SLOT 16
+class cDevice;
+class cCamSlot;
-class cCiCaPidData : public cListObject {
-public:
- bool active;
- int pid;
- int streamType;
- cCiCaPidData(int Pid, int StreamType)
- {
- active = false;
- pid = Pid;
- streamType = StreamType;
- }
- };
+enum eModuleStatus { msNone, msReset, msPresent, msReady };
-class cCiCaProgramData : public cListObject {
+class cCiAdapter : public cThread {
+ friend class cCamSlot;
+private:
+ cDevice *assignedDevice;
+ cCamSlot *camSlots[MAX_CAM_SLOTS_PER_ADAPTER];
+ void AddCamSlot(cCamSlot *CamSlot);
+ ///< Adds the given CamSlot to this CI adapter.
+protected:
+ virtual void Action(void);
+ ///< Handles the attached CAM slots in a separate thread.
+ ///< The derived class must call the Start() function to
+ ///< actually start CAM handling.
+ virtual int Read(uint8_t *Buffer, int MaxLength) = 0;
+ ///< Reads one chunk of data into the given Buffer, up to MaxLength bytes.
+ ///< If no data is available immediately, wait for up to CAM_READ_TIMEOUT.
+ ///< Returns the number of bytes read (in case of an error it will also
+ ///< return 0).
+ virtual void Write(const uint8_t *Buffer, int Length) = 0;
+ ///< Writes Length bytes of the given Buffer.
+ virtual bool Reset(int Slot) = 0;
+ ///< Resets the CAM in the given Slot.
+ ///< Returns true if the operation was successful.
+ virtual eModuleStatus ModuleStatus(int Slot) = 0;
+ ///< Returns the status of the CAM in the given Slot.
+ virtual bool Assign(cDevice *Device, bool Query = false) = 0;
+ ///< Assigns this adapter to the given Device, if this is possible.
+ ///< If Query is 'true', the adapter only checks whether it can be
+ ///< assigned to the Device, but doesn't actually assign itself to it.
+ ///< Returns true if the adapter can be assigned to the Device.
+ ///< If Device is NULL, the adapter will be unassigned from any
+ ///< device it was previously assigned to. The value of Query
+ ///< is ignored in that case, and this function always returns
+ ///< 'true'.
public:
- int programNumber;
- cList<cCiCaPidData> pidList;
- cCiCaProgramData(int ProgramNumber)
- {
- programNumber = ProgramNumber;
- }
+ cCiAdapter(void);
+ virtual ~cCiAdapter();
+ ///< The derived class must call Cancel(3) in its destructor.
+ virtual bool Ready(void);
+ ///< Returns 'true' if all present CAMs in this adapter are ready.
};
-class cCiSession;
-class cCiTransportLayer;
+class cTPDU;
class cCiTransportConnection;
+class cCiSession;
+class cCiCaProgramData;
-class cCiHandler {
+class cCamSlot : public cListObject {
+ friend class cCiAdapter;
+ friend class cCiTransportConnection;
private:
cMutex mutex;
- int fd;
- int numSlots;
- bool newCaSupport;
- bool hasUserIO;
- bool moduleReady[MAX_CI_SLOT];
- cCiSession *sessions[MAX_CI_SESSION];
- cCiTransportLayer *tpl;
- cCiTransportConnection *tc;
+ cCondVar processed;
+ cCiAdapter *ciAdapter;
+ int slotIndex;
+ int slotNumber;
+ cCiTransportConnection *tc[MAX_CONNECTIONS_PER_CAM_SLOT + 1]; // connection numbering starts with 1
+ eModuleStatus lastModuleStatus;
+ time_t resetTime;
+ cTimeMs moduleCheckTimer;
+ bool resendPmt;
int source;
int transponder;
cList<cCiCaProgramData> caProgramList;
- uint32_t ResourceIdToInt(const uint8_t *Data);
- bool Send(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId = 0, int Status = -1);
- const unsigned short *GetCaSystemIds(int Slot);
- cCiSession *GetSessionBySessionId(uint16_t SessionId);
- cCiSession *GetSessionByResourceId(uint32_t ResourceId, int Slot);
- cCiSession *CreateSession(uint32_t ResourceId);
- bool OpenSession(int Length, const uint8_t *Data);
- bool CloseSession(uint16_t SessionId);
- int CloseAllSessions(int Slot);
- cCiHandler(int Fd, int NumSlots);
- void SendCaPmt(void);
+ const int *GetCaSystemIds(void);
+ void SendCaPmt(uint8_t CmdId);
+ void NewConnection(void);
+ void DeleteAllConnections(void);
+ void Process(cTPDU *TPDU = NULL);
+ void Write(cTPDU *TPDU);
+ cCiSession *GetSessionByResourceId(uint32_t ResourceId);
public:
- ~cCiHandler();
- static cCiHandler *CreateCiHandler(const char *FileName);
- ///< Creates a new cCiHandler for the given CA device.
- int NumSlots(void) { return numSlots; }
- ///< Returns the number of CAM slots provided by this CA device.
- int NumCams(void);
- ///< Returns the number of actual CAMs inserted into this CA device.
+ cCamSlot(cCiAdapter *CiAdapter);
+ ///< Creates a new CAM slot for the given CiAdapter.
+ ///< The CiAdapter will take care of deleting the CAM slot,
+ ///< so the caller must not delete it!
+ virtual ~cCamSlot();
+ bool Assign(cDevice *Device, bool Query = false);
+ ///< Assigns this CAM slot to the given Device, if this is possible.
+ ///< If Query is 'true', the CI adapter of this slot only checks whether
+ ///< it can be assigned to the Device, but doesn't actually assign itself to it.
+ ///< Returns true if this slot can be assigned to the Device.
+ ///< If Device is NULL, the slot will be unassigned from any
+ ///< device it was previously assigned to. The value of Query
+ ///< is ignored in that case, and this function always returns
+ ///< 'true'.
+ cDevice *Device(void);
+ ///< Returns the device this CAM slot is currently assigned to.
+ int SlotIndex(void) { return slotIndex; }
+ ///< Returns the index of this CAM slot within its CI adapter.
+ ///< The first slot has an index of 0.
+ int SlotNumber(void) { return slotNumber; }
+ ///< Returns the number of this CAM slot within the whole system.
+ ///< The first slot has the number 1.
+ bool Reset(void);
+ ///< Resets the CAM in this slot.
+ ///< Returns true if the operation was successful.
+ eModuleStatus ModuleStatus(void);
+ ///< Returns the status of the CAM in this slot.
+ const char *GetCamName(void);
+ ///< Returns the name of the CAM in this slot, or NULL if there is
+ ///< no ready CAM in this slot.
bool Ready(void);
- ///< Returns true if all CAMs in this CA device are ready.
- bool Process(int Slot = -1);
- ///< Processes the given Slot. If Slot is -1, all slots are processed.
- ///< Returns false in case of an error.
- bool HasUserIO(void) { return hasUserIO; }
+ ///< Returns 'true' if the CAM in this slot is ready to decrypt.
+ bool HasMMI(void);
+ ///< Returns 'true' if the CAM in this slot has an active MMI.
+ bool HasUserIO(void);
///< Returns true if there is a pending user interaction, which shall
///< be retrieved via GetMenu() or GetEnquiry().
- bool EnterMenu(int Slot);
- ///< Requests the CAM in the given Slot to start its menu.
+ bool EnterMenu(void);
+ ///< Requests the CAM in this slot to start its menu.
cCiMenu *GetMenu(void);
///< Gets a pending menu, or NULL if there is no menu.
cCiEnquiry *GetEnquiry(void);
///< Gets a pending enquiry, or NULL if there is no enquiry.
- const char *GetCamName(int Slot);
- ///< Returns the name of the CAM in the given Slot, or NULL if there
- ///< is no CAM in that slot.
- bool ProvidesCa(const unsigned short *CaSystemIds); //XXX Slot???
- ///< Returns true if any of the CAMs can provide one of the given
+ int Priority(void);
+ ///< Returns the priority if the device this slot is currently assigned
+ ///< to, or -1 if it is not assigned to any device.
+ bool ProvidesCa(const int *CaSystemIds);
+ ///< Returns true if the CAM in this slot provides one of the given
///< CaSystemIds. This doesn't necessarily mean that it will be
///< possible to actually decrypt such a programme, since CAMs
///< usually advertise several CA system ids, while the actual
///< decryption is controlled by the smart card inserted into
///< the CAM.
- void SetSource(int Source, int Transponder);
- ///< Sets the Source and Transponder of the device this cCiHandler is
- ///< currently tuned to. If Source or Transponder are different than
- ///< what was given in a previous call to SetSource(), any previously
- ///< added PIDs will be cleared.
void AddPid(int ProgramNumber, int Pid, int StreamType);
///< Adds the given PID information to the list of PIDs. A later call
///< to SetPid() will (de)activate one of these entries.
@@ -165,16 +207,55 @@ public:
///< Sets the given Pid (which has previously been added through a
///< call to AddPid()) to Active. A later call to StartDecrypting() will
///< send the full list of currently active CA_PMT entries to the CAM.
- bool CanDecrypt(int ProgramNumber);
- ///< XXX
- ///< Returns true if there is a CAM in this CA device that is able
- ///< to decrypt the programme with the given ProgramNumber. The PIDs
- ///< for this ProgramNumber must have been set through previous calls
- ///< to SetPid().
+ void AddChannel(const cChannel *Channel);
+ ///< Adds all PIDs if the given Channel to the current list of PIDs.
+ ///< If the source or transponder of the channel are different than
+ ///< what was given in a previous call to AddChannel(), any previously
+ ///< added PIDs will be cleared.
+ bool CanDecrypt(const cChannel *Channel);
+ ///< Returns true if there is a CAM in this slot that is able to decrypt
+ ///< the given Channel (or at least claims to be able to do so).
+ ///< Since the QUERY/REPLY mechanism for CAMs is pretty unreliable (some
+ ///< CAMs don't reply to queries at all), we always return true if the
+ ///< CAM is currently not decrypting anything. If there is already a
+ ///< channel being decrypted, a call to CanDecrypt() checks whether the
+ ///< CAM can also decrypt the given channel. Only CAMs that have replied
+ ///< to the inital QUERY will perform this check at all. CAMs that never
+ ///< replied to the initial QUERY are assumed not to be able to handle
+ ///< more than one channel at a time.
void StartDecrypting(void);
///< Triggers sending all currently active CA_PMT entries to the CAM,
///< so that it will start decrypting.
- bool Reset(int Slot);
+ void StopDecrypting(void);
+ ///< Clears the list of CA_PMT entries and tells the CAM to stop decrypting.
+ bool IsDecrypting(void);
+ ///< Returns true if the CAM in this slot is currently used for decrypting.
};
+class cCamSlots : public cList<cCamSlot> {};
+
+extern cCamSlots CamSlots;
+
+class cChannelCamRelation;
+
+class cChannelCamRelations : public cList<cChannelCamRelation> {
+private:
+ cMutex mutex;
+ cChannelCamRelation *GetEntry(tChannelID ChannelID);
+ cChannelCamRelation *AddEntry(tChannelID ChannelID);
+ time_t lastCleanup;
+ void Cleanup(void);
+public:
+ cChannelCamRelations(void);
+ void Reset(int CamSlotNumber);
+ bool CamChecked(tChannelID ChannelID, int CamSlotNumber);
+ bool CamDecrypt(tChannelID ChannelID, int CamSlotNumber);
+ void SetChecked(tChannelID ChannelID, int CamSlotNumber);
+ void SetDecrypt(tChannelID ChannelID, int CamSlotNumber);
+ void ClrChecked(tChannelID ChannelID, int CamSlotNumber);
+ void ClrDecrypt(tChannelID ChannelID, int CamSlotNumber);
+ };
+
+extern cChannelCamRelations ChannelCamRelations;
+
#endif //__CI_H
diff --git a/config.h b/config.h
index 3597ff78..e795ad77 100644
--- a/config.h
+++ b/config.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.h 1.282 2007/01/07 13:45:19 kls Exp $
+ * $Id: config.h 1.283 2007/01/07 14:09:31 kls Exp $
*/
#ifndef __CONFIG_H
@@ -21,13 +21,13 @@
// VDR's own version number:
-#define VDRVERSION "1.4.5"
-#define VDRVERSNUM 10405 // Version * 10000 + Major * 100 + Minor
+#define VDRVERSION "1.5.0"
+#define VDRVERSNUM 10500 // Version * 10000 + Major * 100 + Minor
// The plugin API's version number:
-#define APIVERSION "1.4.5"
-#define APIVERSNUM 10405 // Version * 10000 + Major * 100 + Minor
+#define APIVERSION "1.5.0"
+#define APIVERSNUM 10500 // Version * 10000 + Major * 100 + Minor
// When loading plugins, VDR searches them by their APIVERSION, which
// may be smaller than VDRVERSION in case there have been no changes to
diff --git a/device.c b/device.c
index 0fb4fc75..a9ff0afb 100644
--- a/device.c
+++ b/device.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: device.c 1.137 2006/09/03 10:13:25 kls Exp $
+ * $Id: device.c 1.138 2007/01/07 14:41:07 kls Exp $
*/
#include "device.h"
@@ -164,7 +164,9 @@ cDevice::cDevice(void)
sdtFilter = NULL;
nitFilter = NULL;
- ciHandler = NULL;
+ camSlot = NULL;
+ startScrambleDetection = 0;
+
player = NULL;
pesAssembler = new cPesAssembler;
ClrAvailableTracks();
@@ -183,9 +185,7 @@ cDevice::cDevice(void)
cDevice::~cDevice()
{
Detach(player);
- for (int i = 0; i < MAXRECEIVERS; i++)
- Detach(receiver[i]);
- delete ciHandler;
+ DetachAllReceivers();
delete nitFilter;
delete sdtFilter;
delete patFilter;
@@ -199,8 +199,10 @@ bool cDevice::WaitForAllDevicesReady(int Timeout)
for (time_t t0 = time(NULL); time(NULL) - t0 < Timeout; ) {
bool ready = true;
for (int i = 0; i < numDevices; i++) {
- if (device[i] && !device[i]->Ready())
+ if (device[i] && !device[i]->Ready()) {
ready = false;
+ cCondWait::SleepMs(100);
+ }
}
if (ready)
return true;
@@ -278,39 +280,98 @@ cDevice *cDevice::GetDevice(int Index)
return (0 <= Index && Index < numDevices) ? device[Index] : NULL;
}
-cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers)
-{
- cDevice *d = NULL;
- uint Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
- for (int i = 0; i < numDevices; i++) {
- bool ndr;
- if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
- // Put together an integer number that reflects the "impact" using
- // this device would have on the overall system. Each condition is represented
- // by one bit in the number (or several bits, if the condition is actually
- // a numeric value). The sequence in which the conditions are listed corresponds
- // to their individual severity, where the one listed first will make the most
- // difference, because it results in the most significant bit of the result.
- uint imp = 0;
- imp <<= 1; imp |= !device[i]->Receiving() || ndr; // use receiving devices if we don't need to detach existing receivers
- imp <<= 1; imp |= device[i]->Receiving(); // avoid devices that are receiving
- imp <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
- imp <<= 8; imp |= min(max(device[i]->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
- imp <<= 8; imp |= min(max(device[i]->ProvidesCa(Channel), 0), 0xFF); // use the device that provides the lowest number of conditional access methods
- imp <<= 1; imp |= device[i]->IsPrimaryDevice(); // avoid the primary device
- imp <<= 1; imp |= device[i]->HasDecoder(); // avoid full featured cards
- if (imp < Impact) {
- // This device has less impact than any previous one, so we take it.
- Impact = imp;
- d = device[i];
- if (NeedsDetachReceivers)
- *NeedsDetachReceivers = ndr;
+cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView)
+{
+ // Collect the current priorities of all CAM slots that can decrypt the channel:
+ int NumCamSlots = CamSlots.Count();
+ int SlotPriority[NumCamSlots];
+ int NumUsableSlots = 0;
+ if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
+ for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
+ SlotPriority[CamSlot->Index()] = MAXPRIORITY + 1; // assumes it can't be used
+ if (CamSlot->ModuleStatus() == msReady) {
+ if (CamSlot->ProvidesCa(Channel->Caids())) {
+ if (!ChannelCamRelations.CamChecked(Channel->GetChannelID(), CamSlot->SlotNumber())) {
+ SlotPriority[CamSlot->Index()] = CamSlot->Priority();
+ NumUsableSlots++;
+ }
+ }
}
}
+ if (!NumUsableSlots)
+ return NULL; // no CAM is able to decrypt this channel
+ }
+
+ bool NeedsDetachReceivers = false;
+ cDevice *d = NULL;
+ cCamSlot *s = NULL;
+
+ uint32_t Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
+ for (int j = 0; j < NumCamSlots || !NumUsableSlots; j++) {
+ if (NumUsableSlots && SlotPriority[j] > MAXPRIORITY)
+ continue; // there is no CAM available in this slot
+ for (int i = 0; i < numDevices; i++) {
+ if (Channel->Ca() && Channel->Ca() <= CA_DVB_MAX && Channel->Ca() != device[i]->CardIndex() + 1)
+ continue; // a specific card was requested, but not this one
+ if (NumUsableSlots && !CamSlots.Get(j)->Assign(device[i], true))
+ continue; // CAM slot can't be used with this device
+ bool ndr;
+ if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
+ if (NumUsableSlots && device[i]->CamSlot() && device[i]->CamSlot() != CamSlots.Get(j))
+ ndr = true; // using a different CAM slot requires detaching receivers
+ // Put together an integer number that reflects the "impact" using
+ // this device would have on the overall system. Each condition is represented
+ // by one bit in the number (or several bits, if the condition is actually
+ // a numeric value). The sequence in which the conditions are listed corresponds
+ // to their individual severity, where the one listed first will make the most
+ // difference, because it results in the most significant bit of the result.
+ uint32_t imp = 0;
+ imp <<= 1; imp |= LiveView ? !device[i]->IsPrimaryDevice() || ndr : 0; // prefer the primary device for live viewing if we don't need to detach existing receivers
+ imp <<= 1; imp |= !device[i]->Receiving() || ndr; // use receiving devices if we don't need to detach existing receivers
+ imp <<= 1; imp |= device[i]->Receiving(); // avoid devices that are receiving
+ imp <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
+ imp <<= 8; imp |= min(max(device[i]->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
+ imp <<= 8; imp |= min(max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
+ imp <<= 1; imp |= ndr; // avoid devices if we need to detach existing receivers
+ imp <<= 1; imp |= device[i]->IsPrimaryDevice(); // avoid the primary device
+ imp <<= 1; imp |= device[i]->HasDecoder(); // avoid full featured cards
+ imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
+ if (imp < Impact) {
+ // This device has less impact than any previous one, so we take it.
+ Impact = imp;
+ d = device[i];
+ NeedsDetachReceivers = ndr;
+ if (NumUsableSlots)
+ s = CamSlots.Get(j);
+ }
+ }
+ }
+ if (!NumUsableSlots)
+ break; // no CAM necessary, so just one loop over the devices
}
+ if (d) {
+ if (NeedsDetachReceivers)
+ d->DetachAllReceivers();
+ if (s) {
+ if (s->Device() != d) {
+ if (s->Device())
+ s->Device()->DetachAllReceivers();
+ if (d->CamSlot())
+ d->CamSlot()->Assign(NULL);
+ s->Assign(d);
+ }
+ }
+ else if (d->CamSlot() && !d->CamSlot()->IsDecrypting())
+ d->CamSlot()->Assign(NULL);
+ }
return d;
}
+void cDevice::SetCamSlot(cCamSlot *CamSlot)
+{
+ camSlot = CamSlot;
+}
+
void cDevice::Shutdown(void)
{
primaryDevice = NULL;
@@ -422,8 +483,8 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
DelPid(Pid, PidType);
return false;
}
- if (ciHandler)
- ciHandler->SetPid(Pid, true);
+ if (camSlot)
+ camSlot->SetPid(Pid, true);
}
PRINTPIDS("a");
return true;
@@ -451,8 +512,8 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
DelPid(Pid, PidType);
return false;
}
- if (ciHandler)
- ciHandler->SetPid(Pid, true);
+ if (camSlot)
+ camSlot->SetPid(Pid, true);
}
}
return true;
@@ -479,8 +540,8 @@ void cDevice::DelPid(int Pid, ePidType PidType)
if (pidHandles[n].used == 0) {
pidHandles[n].handle = -1;
pidHandles[n].pid = 0;
- if (ciHandler)
- ciHandler->SetPid(Pid, false);
+ if (camSlot)
+ camSlot->SetPid(Pid, false);
}
}
PRINTPIDS("E");
@@ -557,8 +618,10 @@ bool cDevice::MaySwitchTransponder(void)
bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView)
{
- if (LiveView)
+ if (LiveView) {
isyslog("switching to channel %d", Channel->Number());
+ cControl::Shutdown(); // prevents old channel from being shown too long if GetDevice() takes longer
+ }
for (int i = 3; i--;) {
switch (SetChannel(Channel, LiveView)) {
case scrOk: return true;
@@ -578,12 +641,13 @@ bool cDevice::SwitchChannel(int Direction)
bool result = false;
Direction = sgn(Direction);
if (Direction) {
+ cControl::Shutdown(); // prevents old channel from being shown too long if GetDevice() takes longer
int n = CurrentChannel() + Direction;
int first = n;
cChannel *channel;
while ((channel = Channels.GetByNumber(n, Direction)) != NULL) {
// try only channels which are currently available
- if (PrimaryDevice()->ProvidesChannel(channel, Setup.PrimaryLimit) || PrimaryDevice()->CanReplay() && GetDevice(channel, 0))
+ if (GetDevice(channel, 0, true))
break;
n = channel->Number() + Direction;
}
@@ -607,14 +671,9 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
if (LiveView)
StopReplay();
- // If this card is switched to an other transponder, any receivers still
- // attached to it need to be automatically detached:
- bool NeedsDetachReceivers = false;
+ cDevice *Device = (LiveView && IsPrimaryDevice()) ? GetDevice(Channel, 0, LiveView) : this;
- // If this card can't receive this channel, we must not actually switch
- // the channel here, because that would irritate the driver when we
- // start replaying in Transfer Mode immediately after switching the channel:
- bool NeedsTransferMode = (LiveView && IsPrimaryDevice() && !ProvidesChannel(Channel, Setup.PrimaryLimit, &NeedsDetachReceivers));
+ bool NeedsTransferMode = Device != this;
eSetChannelResult Result = scrOk;
@@ -622,14 +681,10 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
// use the card that actually can receive it and transfer data from there:
if (NeedsTransferMode) {
- cDevice *CaDevice = GetDevice(Channel, 0, &NeedsDetachReceivers);
- if (CaDevice && CanReplay()) {
+ if (Device && CanReplay()) {
cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel
- if (CaDevice->SetChannel(Channel, false) == scrOk) { // calling SetChannel() directly, not SwitchChannel()!
- if (NeedsDetachReceivers)
- CaDevice->DetachAllReceivers();
- cControl::Launch(new cTransferControl(CaDevice, Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
- }
+ if (Device->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
+ cControl::Launch(new cTransferControl(Device, Channel->GetChannelID(), Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
else
Result = scrNoTransfer;
}
@@ -644,27 +699,10 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
sectionHandler->SetStatus(false);
sectionHandler->SetChannel(NULL);
}
- // Tell the ciHandler about the channel switch and add all PIDs of this
+ // Tell the camSlot about the channel switch and add all PIDs of this
// channel to it, for possible later decryption:
- if (ciHandler) {
- ciHandler->SetSource(Channel->Source(), Channel->Transponder());
-// Men at work - please stand clear! ;-)
-#ifdef XXX_DO_MULTIPLE_CA_CHANNELS
- if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
-#endif
- ciHandler->AddPid(Channel->Sid(), Channel->Vpid(), 2);
- for (const int *Apid = Channel->Apids(); *Apid; Apid++)
- ciHandler->AddPid(Channel->Sid(), *Apid, 4);
- for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
- ciHandler->AddPid(Channel->Sid(), *Dpid, 0);
-#ifdef XXX_DO_MULTIPLE_CA_CHANNELS
- bool CanDecrypt = ciHandler->CanDecrypt(Channel->Sid());//XXX
- dsyslog("CanDecrypt %d %d %d %s", CardIndex() + 1, CanDecrypt, Channel->Number(), Channel->Name());//XXX
- }
-#endif
- }
- if (NeedsDetachReceivers)
- DetachAllReceivers();
+ if (camSlot)
+ camSlot->AddChannel(Channel);
if (SetChannelDevice(Channel, LiveView)) {
// Start section handling:
if (sectionHandler) {
@@ -672,8 +710,8 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
sectionHandler->SetStatus(true);
}
// Start decrypting any PIDs that might have been set in SetChannelDevice():
- if (ciHandler)
- ciHandler->StartDecrypting();
+ if (camSlot)
+ camSlot->StartDecrypting();
}
else
Result = scrFailed;
@@ -968,9 +1006,10 @@ bool cDevice::AttachPlayer(cPlayer *Player)
void cDevice::Detach(cPlayer *Player)
{
if (Player && player == Player) {
- player->Activate(false);
- player->device = NULL;
- player = NULL;
+ cPlayer *p = player;
+ player = NULL; // avoids recursive calls to Detach()
+ p->Activate(false);
+ p->device = NULL;
SetPlayMode(pmNone);
SetVideoDisplayFormat(eVideoDisplayFormat(Setup.VideoDisplayFormat));
Audios.ClearAudio();
@@ -1143,16 +1182,6 @@ int cDevice::PlayPes(const uchar *Data, int Length, bool VideoOnly)
return Length;
}
-int cDevice::Ca(void) const
-{
- int ca = 0;
- for (int i = 0; i < MAXRECEIVERS; i++) {
- if (receiver[i] && (ca = receiver[i]->ca) != 0)
- break; // all receivers have the same ca
- }
- return ca;
-}
-
int cDevice::Priority(void) const
{
int priority = IsPrimaryDevice() ? Setup.PrimaryLimit - 1 : DEFAULTPRIORITY;
@@ -1168,16 +1197,6 @@ bool cDevice::Ready(void)
return true;
}
-int cDevice::ProvidesCa(const cChannel *Channel) const
-{
- int Ca = Channel->Ca();
- if (Ca == CardIndex() + 1)
- return 1; // exactly _this_ card was requested
- if (Ca && Ca <= CA_DVB_MAX)
- return 0; // a specific card was requested, but not _this_ one
- return !Ca; // by default every card can provide FTA
-}
-
bool cDevice::Receiving(bool CheckAny) const
{
for (int i = 0; i < MAXRECEIVERS; i++) {
@@ -1187,6 +1206,10 @@ bool cDevice::Receiving(bool CheckAny) const
return false;
}
+#define TS_SCRAMBLING_CONTROL 0xC0
+#define TS_SCRAMBLING_TIMEOUT 3 // seconds to wait until a TS becomes unscrambled
+#define TS_SCRAMBLING_TIME_OK 10 // seconds before a Channel/CAM combination is marked a known to decrypt
+
void cDevice::Action(void)
{
if (Running() && OpenDvr()) {
@@ -1196,11 +1219,39 @@ void cDevice::Action(void)
if (GetTSPacket(b)) {
if (b) {
int Pid = (((uint16_t)b[1] & PID_MASK_HI) << 8) | b[2];
+ // Check whether the TS packets are scrambled:
+ bool DetachReceivers = false;
+ bool DescramblingOk = false;
+ int CamSlotNumber = 0;
+ if (startScrambleDetection) {
+ cCamSlot *cs = CamSlot();
+ CamSlotNumber = cs ? cs->SlotNumber() : 0;
+ if (CamSlotNumber) {
+ bool Scrambled = b[3] & TS_SCRAMBLING_CONTROL;
+ int t = time(NULL) - startScrambleDetection;
+ if (Scrambled) {
+ if (t > TS_SCRAMBLING_TIMEOUT)
+ DetachReceivers = true;
+ }
+ else if (t > TS_SCRAMBLING_TIME_OK) {
+ DescramblingOk = true;
+ startScrambleDetection = 0;
+ }
+ }
+ }
// Distribute the packet to all attached receivers:
Lock();
for (int i = 0; i < MAXRECEIVERS; i++) {
- if (receiver[i] && receiver[i]->WantsPid(Pid))
- receiver[i]->Receive(b, TS_SIZE);
+ if (receiver[i] && receiver[i]->WantsPid(Pid)) {
+ if (DetachReceivers) {
+ ChannelCamRelations.SetChecked(receiver[i]->ChannelID(), CamSlotNumber);
+ Detach(receiver[i]);
+ }
+ else
+ receiver[i]->Receive(b, TS_SIZE);
+ if (DescramblingOk)
+ ChannelCamRelations.SetDecrypt(receiver[i]->ChannelID(), CamSlotNumber);
+ }
}
Unlock();
}
@@ -1256,10 +1307,11 @@ bool cDevice::AttachReceiver(cReceiver *Receiver)
Receiver->device = this;
receiver[i] = Receiver;
Unlock();
- if (!Running())
- Start();
- if (ciHandler)
- ciHandler->StartDecrypting();
+ if (camSlot) {
+ camSlot->StartDecrypting();
+ startScrambleDetection = time(NULL);
+ }
+ Start();
return true;
}
}
@@ -1286,10 +1338,10 @@ void cDevice::Detach(cReceiver *Receiver)
else if (receiver[i])
receiversLeft = true;
}
- if (ciHandler)
- ciHandler->StartDecrypting();
+ if (camSlot)
+ camSlot->StartDecrypting();
if (!receiversLeft)
- Cancel(3);
+ Cancel(-1);
}
void cDevice::DetachAll(int Pid)
@@ -1307,10 +1359,8 @@ void cDevice::DetachAll(int Pid)
void cDevice::DetachAllReceivers(void)
{
cMutexLock MutexLock(&mutexReceiver);
- for (int i = 0; i < MAXRECEIVERS; i++) {
- if (receiver[i])
- Detach(receiver[i]);
- }
+ for (int i = 0; i < MAXRECEIVERS; i++)
+ Detach(receiver[i]);
}
// --- cTSBuffer -------------------------------------------------------------
diff --git a/device.h b/device.h
index dced092e..63af410b 100644
--- a/device.h
+++ b/device.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: device.h 1.79 2006/06/15 09:32:48 kls Exp $
+ * $Id: device.h 1.80 2007/01/07 14:38:00 kls Exp $
*/
#ifndef __DEVICE_H
@@ -128,12 +128,21 @@ public:
///< Gets the device with the given Index.
///< \param Index must be in the range 0..numDevices-1.
///< \return A pointer to the device, or NULL if the Index was invalid.
- static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
+ static cDevice *GetDevice(const cChannel *Channel, int Priority, bool LiveView);
///< Returns a device that is able to receive the given Channel at the
///< given Priority, with the least impact on active recordings and
- ///< live viewing.
- ///< See ProvidesChannel() for more information on how
- ///< priorities are handled, and the meaning of NeedsDetachReceivers.
+ ///< live viewing. The LiveView parameter tells whether the device will
+ ///< be used for live viewing or a recording.
+ ///< If the Channel is encrypted, a CAM slot that claims to be able to
+ ///< decrypt the channel is automatically selected and assigned to the
+ ///< returned device. Whether or not this combination of device and CAM
+ ///< slot is actually able to decrypt the channel can only be determined
+ ///< by checking the "scrambling control" bits of the received TS packets.
+ ///< The Action() function automatically does this and takes care that
+ ///< after detaching any receivers because the channel can't be decrypted,
+ ///< this device/CAM combination will be skipped in the next call to
+ ///< GetDevice().
+ ///< See also ProvidesChannel().
static void Shutdown(void);
///< Closes down all devices.
///< Must be called at the end of the program.
@@ -171,16 +180,6 @@ public:
///< Returns the card index of this device (0 ... MAXDEVICES - 1).
int DeviceNumber(void) const;
///< Returns the number of this device (0 ... numDevices).
- virtual int ProvidesCa(const cChannel *Channel) const;
- ///< Checks whether this device provides the conditional access
- ///< facilities to decrypt the given Channel.
- ///< Returns 0 if the Channel can't be decrypted, 1 if this is a
- ///< Free To Air channel or only exactly this device can decrypt it,
- ///< and > 1 if this device can decrypt the Channel.
- ///< If the result is greater than 1 and the device has more than one
- ///< CAM, the value will be increased by the number of CAMs, which
- ///< allows to select the device with the smallest number of CAMs
- ///< in order to preserve resources for other recordings.
virtual bool HasDecoder(void) const;
///< Tells whether this device has an MPEG decoder.
@@ -199,7 +198,9 @@ public:
virtual bool ProvidesSource(int Source) const;
///< Returns true if this device can provide the given source.
virtual bool ProvidesTransponder(const cChannel *Channel) const;
- ///< XXX -> PLUGINS.html!
+ ///< Returns true if this device can provide the transponder of the
+ ///< given Channel (which implies that it can provide the Channel's
+ ///< source).
virtual bool ProvidesTransponderExclusively(const cChannel *Channel) const;
///< Returns true if this is the only device that is able to provide
///< the given channel's transponder.
@@ -246,7 +247,7 @@ public:
///< channel number while replaying.
void ForceTransferMode(void);
///< Forces the device into transfermode for the current channel.
- virtual bool HasLock(int TimeoutMs = 0);//XXX PLUGINS.html
+ virtual bool HasLock(int TimeoutMs = 0);
///< Returns true if the device has a lock on the requested transponder.
///< Default is true, a specific device implementation may return false
///< to indicate that it is not ready yet.
@@ -309,10 +310,15 @@ public:
// Common Interface facilities:
-protected:
- cCiHandler *ciHandler;
+private:
+ time_t startScrambleDetection;
+ cCamSlot *camSlot;
public:
- cCiHandler *CiHandler(void) { return ciHandler; }
+ void SetCamSlot(cCamSlot *CamSlot);
+ ///< Sets the given CamSlot to be used with this device.
+ cCamSlot *CamSlot(void) const { return camSlot; }
+ ///< Returns the CAM slot that is currently used with this device,
+ ///< or NULL if no CAM slot is in use.
// Image Grab facilities
@@ -512,11 +518,12 @@ public:
private:
cMutex mutexReceiver;
cReceiver *receiver[MAXRECEIVERS];
-protected:
+public:
int Priority(void) const;
///< Returns the priority of the current receiving session (0..MAXPRIORITY),
///< or -1 if no receiver is currently active. The primary device will
///< always return at least Setup.PrimaryLimit-1.
+protected:
virtual bool OpenDvr(void);
///< Opens the DVR of this device and prepares it to deliver a Transport
///< Stream for use in a cReceiver.
@@ -530,8 +537,6 @@ protected:
///< false in case of a non recoverable error, otherwise it returns true,
///< even if Data is NULL.
public:
- int Ca(void) const;
- ///< Returns the ca of the current receiving session(s).
bool Receiving(bool CheckAny = false) const;
///< Returns true if we are currently receiving.
bool AttachReceiver(cReceiver *Receiver);
diff --git a/dvbci.c b/dvbci.c
new file mode 100644
index 00000000..744b116b
--- /dev/null
+++ b/dvbci.c
@@ -0,0 +1,108 @@
+/*
+ * dvbci.h: Common Interface for DVB devices
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: dvbci.c 1.1 2007/01/07 14:38:00 kls Exp $
+ */
+
+#include "dvbci.h"
+#include <linux/dvb/ca.h>
+#include <sys/ioctl.h>
+#include "device.h"
+
+// --- cDvbCiAdapter ---------------------------------------------------------
+
+cDvbCiAdapter::cDvbCiAdapter(cDevice *Device, int Fd)
+{
+ device = Device;
+ SetDescription("CI adapter on device %d", device->DeviceNumber());
+ fd = Fd;
+ ca_caps_t Caps;
+ if (ioctl(fd, CA_GET_CAP, &Caps) == 0) {
+ if ((Caps.slot_type & CA_CI_LINK) != 0) {
+ int NumSlots = Caps.slot_num;
+ if (NumSlots > 0) {
+ for (int i = 0; i < NumSlots; i++)
+ new cCamSlot(this);
+ Start();
+ }
+ else
+ esyslog("ERROR: no CAM slots found on device %d", device->DeviceNumber());
+ }
+ else
+ isyslog("device %d doesn't support CI link layer interface", device->DeviceNumber());
+ }
+ else
+ esyslog("ERROR: can't get CA capabilities on device %d", device->DeviceNumber());
+}
+
+cDvbCiAdapter::~cDvbCiAdapter()
+{
+ Cancel(3);
+}
+
+int cDvbCiAdapter::Read(uint8_t *Buffer, int MaxLength)
+{
+ if (Buffer && MaxLength > 0) {
+ struct pollfd pfd[1];
+ pfd[0].fd = fd;
+ pfd[0].events = POLLIN;
+ if (poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) {
+ int n = safe_read(fd, Buffer, MaxLength);
+ if (n >= 0)
+ return n;
+ esyslog("ERROR: can't read from CI adapter on device %d: %m", device->DeviceNumber());
+ }
+ }
+ return 0;
+}
+
+void cDvbCiAdapter::Write(const uint8_t *Buffer, int Length)
+{
+ if (Buffer && Length > 0) {
+ if (safe_write(fd, Buffer, Length) != Length)
+ esyslog("ERROR: can't write to CI adapter on device %d: %m", device->DeviceNumber());
+ }
+}
+
+bool cDvbCiAdapter::Reset(int Slot)
+{
+ if (ioctl(fd, CA_RESET, 1 << Slot) != -1)
+ return true;
+ else
+ esyslog("ERROR: can't reset CAM slot %d on device %d: %m", Slot, device->DeviceNumber());
+ return false;
+}
+
+eModuleStatus cDvbCiAdapter::ModuleStatus(int Slot)
+{
+ ca_slot_info_t sinfo;
+ sinfo.num = Slot;
+ if (ioctl(fd, CA_GET_SLOT_INFO, &sinfo) != -1) {
+ if ((sinfo.flags & CA_CI_MODULE_READY) != 0)
+ return msReady;
+ else if ((sinfo.flags & CA_CI_MODULE_PRESENT) != 0)
+ return msPresent;
+ }
+ else
+ esyslog("ERROR: can't get info of CAM slot %d on device %d: %m", Slot, device->DeviceNumber());
+ return msNone;
+}
+
+bool cDvbCiAdapter::Assign(cDevice *Device, bool Query)
+{
+ // The CI is hardwired to its device, so there's not really much to do here
+ if (Device)
+ return Device == device;
+ return true;
+}
+
+cDvbCiAdapter *cDvbCiAdapter::CreateCiAdapter(cDevice *Device, int Fd)
+{
+ // TODO check whether a CI is actually present?
+ if (Device)
+ return new cDvbCiAdapter(Device, Fd);
+ return NULL;
+}
diff --git a/dvbci.h b/dvbci.h
new file mode 100644
index 00000000..e4c740a2
--- /dev/null
+++ b/dvbci.h
@@ -0,0 +1,31 @@
+/*
+ * dvbci.h: Common Interface for DVB devices
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: dvbci.h 1.1 2007/01/07 14:38:00 kls Exp $
+ */
+
+#ifndef __DVBCI_H
+#define __DVBCI_H
+
+#include "ci.h"
+
+class cDvbCiAdapter : public cCiAdapter {
+private:
+ cDevice *device;
+ int fd;
+protected:
+ virtual int Read(uint8_t *Buffer, int MaxLength);
+ virtual void Write(const uint8_t *Buffer, int Length);
+ virtual bool Reset(int Slot);
+ virtual eModuleStatus ModuleStatus(int Slot);
+ virtual bool Assign(cDevice *Device, bool Query = false);
+ cDvbCiAdapter(cDevice *Device, int Fd);
+public:
+ virtual ~cDvbCiAdapter();
+ static cDvbCiAdapter *CreateCiAdapter(cDevice *Device, int Fd);
+ };
+
+#endif //__DVBCI_H
diff --git a/dvbdevice.c b/dvbdevice.c
index fb8ec938..70f7781a 100644
--- a/dvbdevice.c
+++ b/dvbdevice.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbdevice.c 1.160 2006/08/14 09:38:32 kls Exp $
+ * $Id: dvbdevice.c 1.161 2007/01/07 14:09:51 kls Exp $
*/
#include "dvbdevice.h"
@@ -19,6 +19,7 @@
#include <sys/mman.h>
#include "channels.h"
#include "diseqc.h"
+#include "dvbci.h"
#include "dvbosd.h"
#include "eitscan.h"
#include "player.h"
@@ -28,7 +29,6 @@
#define DO_REC_AND_PLAY_ON_PRIMARY_DEVICE 1
#define DO_MULTIPLE_RECORDINGS 1
-//#define DO_MULTIPLE_CA_CHANNELS
#define DEV_VIDEO "/dev/video"
#define DEV_DVB_ADAPTER "/dev/dvb/adapter"
@@ -77,7 +77,6 @@ private:
int lockTimeout;
time_t lastTimeoutReport;
fe_type_t frontendType;
- cCiHandler *ciHandler;
cChannel channel;
const char *diseqcCommands;
eTunerStatus tunerStatus;
@@ -88,19 +87,18 @@ private:
bool SetFrontend(void);
virtual void Action(void);
public:
- cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType, cCiHandler *CiHandler);
+ cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType);
virtual ~cDvbTuner();
bool IsTunedTo(const cChannel *Channel) const;
void Set(const cChannel *Channel, bool Tune);
bool Locked(int TimeoutMs = 0);
};
-cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType, cCiHandler *CiHandler)
+cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType)
{
fd_frontend = Fd_Frontend;
cardIndex = CardIndex;
frontendType = FrontendType;
- ciHandler = CiHandler;
tuneTimeout = 0;
lockTimeout = 0;
lastTimeoutReport = 0;
@@ -346,8 +344,6 @@ void cDvbTuner::Action(void)
}
}
- if (ciHandler)
- ciHandler->Process();
if (tunerStatus != tsTuned)
newSet.TimedWait(mutex, 1000);
}
@@ -360,6 +356,7 @@ int cDvbDevice::setTransferModeForDolbyDigital = 1;
cDvbDevice::cDvbDevice(int n)
{
+ ciAdapter = NULL;
dvbTuner = NULL;
frontendType = fe_type_t(-1); // don't know how else to initialize this - there is no FE_UNKNOWN
spuDecoder = NULL;
@@ -377,6 +374,12 @@ cDvbDevice::cDvbDevice(int n)
fd_audio = DvbOpen(DEV_DVB_AUDIO, n, O_RDWR | O_NONBLOCK);
fd_stc = DvbOpen(DEV_DVB_DEMUX, n, O_RDWR);
+ // Common Interface:
+
+ fd_ca = DvbOpen(DEV_DVB_CA, n, O_RDWR);
+ if (fd_ca >= 0)
+ ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, fd_ca);
+
// The DVR device (will be opened and closed as needed):
fd_dvr = -1;
@@ -420,8 +423,7 @@ cDvbDevice::cDvbDevice(int n)
dvb_frontend_info feinfo;
if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0) {
frontendType = feinfo.type;
- ciHandler = cCiHandler::CreateCiHandler(*cDvbName(DEV_DVB_CA, n));
- dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType, ciHandler);
+ dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType);
}
else
LOG_ERROR;
@@ -436,6 +438,7 @@ cDvbDevice::~cDvbDevice()
{
delete spuDecoder;
delete dvbTuner;
+ delete ciAdapter;
// We're not explicitly closing any device files here, since this sometimes
// caused segfaults. Besides, the program is about to terminate anyway...
}
@@ -494,32 +497,11 @@ bool cDvbDevice::HasDecoder(void) const
bool cDvbDevice::Ready(void)
{
- if (ciHandler) {
- ciHandler->Process();
- return ciHandler->Ready();
- }
+ if (ciAdapter)
+ return ciAdapter->Ready();
return true;
}
-int cDvbDevice::ProvidesCa(const cChannel *Channel) const
-{
- int NumCams = 0;
- if (ciHandler) {
- NumCams = ciHandler->NumCams();
- if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
- unsigned short ids[MAXCAIDS + 1];
- for (int i = 0; i <= MAXCAIDS; i++) // '<=' copies the terminating 0!
- ids[i] = Channel->Ca(i);
- if (ciHandler->ProvidesCa(ids))
- return NumCams + 1;
- }
- }
- int result = cDevice::ProvidesCa(Channel);
- if (result > 0)
- result += NumCams;
- return result;
-}
-
cSpuDecoder *cDvbDevice::GetSpuDecoder(void)
{
if (!spuDecoder && IsPrimaryDevice())
@@ -770,18 +752,19 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
bool hasPriority = Priority < 0 || Priority > this->Priority();
bool needsDetachReceivers = false;
- if (ProvidesSource(Channel->Source()) && ProvidesCa(Channel)) {
+ if (ProvidesSource(Channel->Source())) {
result = hasPriority;
if (Priority >= 0 && Receiving(true)) {
if (dvbTuner->IsTunedTo(Channel)) {
if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0))) {
#ifdef DO_MULTIPLE_RECORDINGS
-#ifndef DO_MULTIPLE_CA_CHANNELS
- if (Ca() >= CA_ENCRYPTED_MIN || Channel->Ca() >= CA_ENCRYPTED_MIN)
- needsDetachReceivers = Ca() != Channel->Ca();
- else
-#endif
- if (!IsPrimaryDevice())
+ if (CamSlot() && Channel->Ca() >= CA_ENCRYPTED_MIN) {
+ if (CamSlot()->CanDecrypt(Channel))
+ result = true;
+ else
+ needsDetachReceivers = true;
+ }
+ else if (!IsPrimaryDevice())
result = true;
#ifdef DO_REC_AND_PLAY_ON_PRIMARY_DEVICE
else
@@ -821,6 +804,8 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
&& (LiveView && HasPid(Channel->Vpid() ? Channel->Vpid() : Channel->Apid(0)) && (pidHandles[ptVideo].pid != Channel->Vpid() || (pidHandles[ptAudio].pid != Channel->Apid(0) && (Channel->Dpid(0) ? pidHandles[ptAudio].pid != Channel->Dpid(0) : true)))// the PID is already set as DMX_PES_OTHER
|| !LiveView && (pidHandles[ptVideo].pid == Channel->Vpid() || pidHandles[ptAudio].pid == Channel->Apid(0)) // a recording is going to shift the PIDs from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
);
+ if (CamSlot() && !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), CamSlot()->SlotNumber()))
+ StartTransferMode |= LiveView && IsPrimaryDevice() && Channel->Ca() >= CA_ENCRYPTED_MIN;
bool TurnOnLivePIDs = HasDecoder() && !StartTransferMode && LiveView;
@@ -861,7 +846,7 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
}
else if (StartTransferMode)
- cControl::Launch(new cTransferControl(this, Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
+ cControl::Launch(new cTransferControl(this, Channel->GetChannelID(), Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
return true;
}
@@ -922,13 +907,13 @@ void cDvbDevice::SetAudioTrackDevice(eTrackType Type)
if (IS_AUDIO_TRACK(Type) || (IS_DOLBY_TRACK(Type) && SetAudioBypass(true))) {
if (pidHandles[ptAudio].pid && pidHandles[ptAudio].pid != TrackId->id) {
DetachAll(pidHandles[ptAudio].pid);
- if (ciHandler)
- ciHandler->SetPid(pidHandles[ptAudio].pid, false);
+ if (CamSlot())
+ CamSlot()->SetPid(pidHandles[ptAudio].pid, false);
pidHandles[ptAudio].pid = TrackId->id;
SetPid(&pidHandles[ptAudio], ptAudio, true);
- if (ciHandler) {
- ciHandler->SetPid(pidHandles[ptAudio].pid, true);
- ciHandler->StartDecrypting();
+ if (CamSlot()) {
+ CamSlot()->SetPid(pidHandles[ptAudio].pid, true);
+ CamSlot()->StartDecrypting();
}
}
}
diff --git a/dvbdevice.h b/dvbdevice.h
index effa5fb9..c7280e9f 100644
--- a/dvbdevice.h
+++ b/dvbdevice.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbdevice.h 1.41 2006/05/28 15:05:19 kls Exp $
+ * $Id: dvbdevice.h 1.42 2007/01/07 14:39:52 kls Exp $
*/
#ifndef __DVBDEVICE_H
@@ -36,16 +36,20 @@ public:
///< \return True if any devices are available.
private:
fe_type_t frontendType;
- int fd_osd, fd_audio, fd_video, fd_dvr, fd_stc;
+ int fd_osd, fd_audio, fd_video, fd_dvr, fd_stc, fd_ca;
protected:
virtual void MakePrimaryDevice(bool On);
public:
cDvbDevice(int n);
virtual ~cDvbDevice();
virtual bool Ready(void);
- virtual int ProvidesCa(const cChannel *Channel) const;
virtual bool HasDecoder(void) const;
+// Common Interface facilities:
+
+private:
+ cCiAdapter *ciAdapter;
+
// SPU facilities
private:
diff --git a/i18n.c b/i18n.c
index 475264b2..73f3d87d 100644
--- a/i18n.c
+++ b/i18n.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: i18n.c 1.285 2006/10/14 09:26:41 kls Exp $
+ * $Id: i18n.c 1.286 2007/01/07 14:19:15 kls Exp $
*
* Translations provided by:
*
@@ -2598,27 +2598,27 @@ const tI18nPhrase Phrases[] = {
"Kan ikke åbne CAM menuen!",
"Menu CAM není dostupné",
},
- { "Resetting CAM...",
- "CAM wird zurückgesetzt...",
- "Resetiram CAM...",
- "Reimpostazione modulo CAM...",
- "CAM wordt herstart...",
+ { "CAM is in use - really reset?"
+ "CAM wird benutzt - wirklich zurücksetzen?",
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
"",//TODO
- "Réinitialisation du CAM",
"",//TODO
- "CA-moduuli palautetaan alkutilaan...",
- "Resetujê CAM...",
- "Reiniciando CAM...",
"",//TODO
- "Återställer CAM ...",
- "Se reseteazã CAM...",
- "A CAM újra indul...",
"",//TODO
- "¿ÕàÕÓàã×ÚÐ CAM...",
"",//TODO
- "CAM mooduli taaskäivitus...",
- "Nulstiller CAM...",
- "Restartuje se CAM...",
},
{ "Can't reset CAM!",
"Zurücksetzen des CAM fehlgeschlagen!",
@@ -2642,27 +2642,93 @@ const tI18nPhrase Phrases[] = {
"Kan ikke nulstille CAM!",
"CAM modul nelze restartovat!",
},
- { "CAM has been reset",
- "CAM wurde zurückgesetzt",
- "CAM je resetiran",
- "Modulo CAM reimpostato",
- "CAM is herstart",
- "",//TODO
- "La CAM a été réinitialisée",
- "",//TODO
- "CA-moduuli palautettu alkutilaan",
- "CAM zosta³ zresetowany",
- "CAM reiniciado",
- "¸ãéíå åðáíáöïñÜ óôï CAM",
- "CA modulen har återställts",
- "CAM-ul a fost resetat",
- "A CAM vissza lett állítva",
- "CAM reiniciada",
- "CAM-ÜÞÔãÛì ßÕàÕ×ÐßãéÕÝ",
- "CAM je resetiran",
- "CAM mooduli taaskäivitus tehtud",
- "CAM er blevet nulstillet",
- "CAM byl restartován",
+ { "CAM reset",
+ "CAM zurückgesetzt",
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ },
+ { "CAM present",
+ "CAM vorhanden",
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ },
+ { "CAM ready",
+ "CAM bereit",
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ },
+ { "CAM not responding!",
+ "CAM antwortet nicht!",
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
+ "",//TODO
},
{ "Please enter %d digits!",
"Bitte geben Sie %d Ziffern ein!",
@@ -2797,27 +2863,27 @@ const tI18nPhrase Phrases[] = {
"LNB",
"LNB",
},
- { "CICAM",
- "CICAM",
- "CICAM",
- "Accesso condizionato CICAM",
- "CICAM",
- "CICAM",
+ { "CAM",
+ "CAM",
+ "CAM",
+ "Accesso condizionato CAM",
+ "CAM",
+ "CAM",
"Accès conditionnel",
- "CICAM",
- "CICAM",
- "CICAM",
- "CICAM",
- "CICAM",
- "CICAM",
- "CICAM",
- "CICAM",
+ "CAM",
+ "CAM",
+ "CAM",
+ "CAM",
+ "CAM",
+ "CAM",
+ "CAM",
+ "CAM",
"CI Accés condicional",
"ÃáÛÞÒÝëÙ ÔÞáâãß",
- "CICAM",
- "CICAM",
- "CICAM",
- "CICAM",
+ "CAM",
+ "CAM",
+ "CAM",
+ "CAM",
},
{ "Recording",
"Aufnahme",
@@ -3964,28 +4030,6 @@ const tI18nPhrase Phrases[] = {
"Anvend DiSEqC",
"Pou¾ívat DiSEqC",
},
- { "Setup.CICAM$CICAM DVB",
- "CICAM-DVB",
- "CICAM DVB",
- "CICAM DVB",
- "CICAM DVB",
- "CICAM DVB",
- "Accès conditionnel",
- "CICAM DVB",
- "CICAM DVB",
- "CICAM DVB",
- "CICAM DVB",
- "CICAM DVB",
- "CICAM DVB",
- "CICAM DVB",
- "CICAM DVB",
- "Accés condicional CICAM",
- "CAM-ÜÞÔãÛì DVB",
- "CICAM DVB",
- "CICAM DVB",
- "CICAM DVB",
- "CICAM DVB",
- },
{ "Setup.Recording$Margin at start (min)",
"Vorlauf zum Timer-Beginn (min)",
"Premik zaèetka snemanja (min)",
diff --git a/menu.c b/menu.c
index 10a3b266..b8bc7c70 100644
--- a/menu.c
+++ b/menu.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.c 1.446 2006/12/02 11:12:02 kls Exp $
+ * $Id: menu.c 1.447 2007/01/07 14:19:48 kls Exp $
*/
#include "menu.h"
@@ -38,7 +38,9 @@
#define MAXRECORDCONTROLS (MAXDEVICES * MAXRECEIVERS)
#define MAXINSTANTRECTIME (24 * 60 - 1) // 23:59 hours
-#define MAXWAITFORCAMMENU 4 // seconds to wait for the CAM menu to open
+#define MAXWAITFORCAMMENU 10 // seconds to wait for the CAM menu to open
+#define CAMMENURETYTIMEOUT 3 // seconds after which opening the CAM menu is retried
+#define CAMRESPONSETIMEOUT 5 // seconds to wait for a response from a CAM
#define MINFREEDISK 300 // minimum free disk space (in MB) required to start recording
#define NODISKSPACEDELTA 300 // seconds between "Not enough disk space to start recording!" messages
@@ -1579,38 +1581,104 @@ eOSState cMenuCommands::ProcessKey(eKeys Key)
// --- cMenuCam --------------------------------------------------------------
-cMenuCam::cMenuCam(cCiMenu *CiMenu)
-:cOsdMenu("")
+class cMenuCam : public cOsdMenu {
+private:
+ cCamSlot *camSlot;
+ cCiMenu *ciMenu;
+ cCiEnquiry *ciEnquiry;
+ char *input;
+ int offset;
+ time_t lastCamExchange;
+ void GenerateTitle(const char *s = NULL);
+ void QueryCam(void);
+ void AddMultiLineItem(const char *s);
+ void Set(void);
+ eOSState Select(void);
+public:
+ cMenuCam(cCamSlot *CamSlot);
+ virtual ~cMenuCam();
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+cMenuCam::cMenuCam(cCamSlot *CamSlot)
+:cOsdMenu("", 1) // tab necessary for enquiry!
{
- dsyslog("CAM: Menu ------------------");
- ciMenu = CiMenu;
- selected = false;
+ camSlot = CamSlot;
+ ciMenu = NULL;
+ ciEnquiry = NULL;
+ input = NULL;
offset = 0;
- if (ciMenu->Selectable())
- SetHasHotkeys();
- SetTitle(*ciMenu->TitleText() ? ciMenu->TitleText() : "CAM");
- dsyslog("CAM: '%s'", ciMenu->TitleText());
- if (*ciMenu->SubTitleText()) {
- dsyslog("CAM: '%s'", ciMenu->SubTitleText());
- AddMultiLineItem(ciMenu->SubTitleText());
- offset = Count();
- }
- for (int i = 0; i < ciMenu->NumEntries(); i++) {
- Add(new cOsdItem(hk(ciMenu->Entry(i)), osUnknown, ciMenu->Selectable()));
- dsyslog("CAM: '%s'", ciMenu->Entry(i));
- }
- if (*ciMenu->BottomText()) {
- AddMultiLineItem(ciMenu->BottomText());
- dsyslog("CAM: '%s'", ciMenu->BottomText());
- }
- Display();
+ lastCamExchange = time(NULL);
+ SetNeedsFastResponse(true);
+ QueryCam();
}
cMenuCam::~cMenuCam()
{
- if (!selected)
+ if (ciMenu)
ciMenu->Abort();
delete ciMenu;
+ if (ciEnquiry)
+ ciEnquiry->Abort();
+ delete ciEnquiry;
+ free(input);
+}
+
+void cMenuCam::GenerateTitle(const char *s)
+{
+ SetTitle(cString::sprintf("CAM %d - %s", camSlot->SlotNumber(), (s && *s) ? s : camSlot->GetCamName()));
+}
+
+void cMenuCam::QueryCam(void)
+{
+ delete ciMenu;
+ ciMenu = NULL;
+ delete ciEnquiry;
+ ciEnquiry = NULL;
+ if (camSlot->HasUserIO()) {
+ ciMenu = camSlot->GetMenu();
+ ciEnquiry = camSlot->GetEnquiry();
+ }
+ Set();
+}
+
+void cMenuCam::Set(void)
+{
+ if (ciMenu) {
+ Clear();
+ free(input);
+ input = NULL;
+ dsyslog("CAM %d: Menu ------------------", camSlot->SlotNumber());
+ offset = 0;
+ SetHasHotkeys(ciMenu->Selectable());
+ GenerateTitle(ciMenu->TitleText());
+ dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciMenu->TitleText());
+ if (*ciMenu->SubTitleText()) {
+ dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciMenu->SubTitleText());
+ AddMultiLineItem(ciMenu->SubTitleText());
+ offset = Count();
+ }
+ for (int i = 0; i < ciMenu->NumEntries(); i++) {
+ Add(new cOsdItem(hk(ciMenu->Entry(i)), osUnknown, ciMenu->Selectable()));
+ dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciMenu->Entry(i));
+ }
+ if (*ciMenu->BottomText()) {
+ AddMultiLineItem(ciMenu->BottomText());
+ dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciMenu->BottomText());
+ }
+ }
+ else if (ciEnquiry) {
+ Clear();
+ int Length = ciEnquiry->ExpectedLength();
+ free(input);
+ input = MALLOC(char, Length + 1);
+ *input = 0;
+ GenerateTitle();
+ Add(new cOsdItem(ciEnquiry->Text(), osUnknown, false));
+ Add(new cOsdItem("", osUnknown, false));
+ Add(new cMenuEditNumItem("", input, Length, ciEnquiry->Blind()));
+ }
+ Display();
}
void cMenuCam::AddMultiLineItem(const char *s)
@@ -1628,90 +1696,61 @@ void cMenuCam::AddMultiLineItem(const char *s)
eOSState cMenuCam::Select(void)
{
- if (ciMenu->Selectable()) {
- ciMenu->Select(Current() - offset);
- dsyslog("CAM: select %d", Current() - offset);
- }
- else
- ciMenu->Cancel();
- selected = true;
- return osEnd;
-}
-
-eOSState cMenuCam::ProcessKey(eKeys Key)
-{
- eOSState state = cOsdMenu::ProcessKey(Key);
-
- if (state == osUnknown) {
- switch (Key) {
- case kOk: return Select();
- default: break;
- }
- }
- else if (state == osBack) {
- ciMenu->Cancel();
- selected = true;
- return osEnd;
+ if (ciMenu) {
+ if (ciMenu->Selectable()) {
+ ciMenu->Select(Current() - offset);
+ dsyslog("CAM %d: select %d", camSlot->SlotNumber(), Current() - offset);
+ }
+ else
+ ciMenu->Cancel();
}
- if (ciMenu->HasUpdate()) {
- selected = true;
- return osEnd;
+ else if (ciEnquiry) {
+ if (ciEnquiry->ExpectedLength() < 0xFF && int(strlen(input)) != ciEnquiry->ExpectedLength()) {
+ char buffer[64];
+ snprintf(buffer, sizeof(buffer), tr("Please enter %d digits!"), ciEnquiry->ExpectedLength());
+ Skins.Message(mtError, buffer);
+ return osContinue;
+ }
+ ciEnquiry->Reply(input);
+ dsyslog("CAM %d: entered '%s'", camSlot->SlotNumber(), ciEnquiry->Blind() ? "****" : input);
}
- return state;
-}
-
-// --- cMenuCamEnquiry -------------------------------------------------------
-
-cMenuCamEnquiry::cMenuCamEnquiry(cCiEnquiry *CiEnquiry)
-:cOsdMenu("", 1)
-{
- ciEnquiry = CiEnquiry;
- int Length = ciEnquiry->ExpectedLength();
- input = MALLOC(char, Length + 1);
- *input = 0;
- replied = false;
- SetTitle("CAM");
- Add(new cOsdItem(ciEnquiry->Text(), osUnknown, false));
- Add(new cOsdItem("", osUnknown, false));
- Add(new cMenuEditNumItem("", input, Length, ciEnquiry->Blind()));
- Display();
-}
-
-cMenuCamEnquiry::~cMenuCamEnquiry()
-{
- if (!replied)
- ciEnquiry->Abort();
- free(input);
- delete ciEnquiry;
+ QueryCam();
+ return osContinue;
}
-eOSState cMenuCamEnquiry::Reply(void)
+eOSState cMenuCam::ProcessKey(eKeys Key)
{
- if (ciEnquiry->ExpectedLength() < 0xFF && int(strlen(input)) != ciEnquiry->ExpectedLength()) {
- char buffer[64];
- snprintf(buffer, sizeof(buffer), tr("Please enter %d digits!"), ciEnquiry->ExpectedLength());
- Skins.Message(mtError, buffer);
- return osContinue;
- }
- ciEnquiry->Reply(input);
- replied = true;
- return osEnd;
-}
+ if (!camSlot->HasMMI())
+ return osBack;
-eOSState cMenuCamEnquiry::ProcessKey(eKeys Key)
-{
eOSState state = cOsdMenu::ProcessKey(Key);
- if (state == osUnknown) {
- switch (Key) {
- case kOk: return Reply();
- default: break;
- }
+ if (ciMenu || ciEnquiry) {
+ lastCamExchange = time(NULL);
+ if (state == osUnknown) {
+ switch (Key) {
+ case kOk: return Select();
+ default: break;
+ }
+ }
+ else if (state == osBack) {
+ if (ciMenu)
+ ciMenu->Cancel();
+ if (ciEnquiry)
+ ciEnquiry->Cancel();
+ QueryCam();
+ return osContinue;
+ }
+ if (ciMenu && ciMenu->HasUpdate()) {
+ QueryCam();
+ return osContinue;
+ }
}
- else if (state == osBack) {
- ciEnquiry->Cancel();
- replied = true;
- return osEnd;
+ else if (time(NULL) - lastCamExchange < CAMRESPONSETIMEOUT)
+ QueryCam();
+ else {
+ Skins.Message(mtError, tr("CAM not responding!"));
+ return osBack;
}
return state;
}
@@ -1720,21 +1759,9 @@ eOSState cMenuCamEnquiry::ProcessKey(eKeys Key)
cOsdObject *CamControl(void)
{
- for (int d = 0; d < cDevice::NumDevices(); d++) {
- cDevice *Device = cDevice::GetDevice(d);
- if (Device) {
- cCiHandler *CiHandler = Device->CiHandler();
- if (CiHandler && CiHandler->HasUserIO()) {
- cCiMenu *CiMenu = CiHandler->GetMenu();
- if (CiMenu)
- return new cMenuCam(CiMenu);
- else {
- cCiEnquiry *CiEnquiry = CiHandler->GetEnquiry();
- if (CiEnquiry)
- return new cMenuCamEnquiry(CiEnquiry);
- }
- }
- }
+ for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
+ if (CamSlot->HasUserIO())
+ return new cMenuCam(CamSlot);
}
return NULL;
}
@@ -2454,95 +2481,117 @@ eOSState cMenuSetupLNB::ProcessKey(eKeys Key)
return state;
}
-// --- cMenuSetupCICAM -------------------------------------------------------
+// --- cMenuSetupCAM ---------------------------------------------------------
-class cMenuSetupCICAMItem : public cOsdItem {
+class cMenuSetupCAMItem : public cOsdItem {
private:
- cCiHandler *ciHandler;
- int slot;
+ cCamSlot *camSlot;
public:
- cMenuSetupCICAMItem(int Device, cCiHandler *CiHandler, int Slot);
- cCiHandler *CiHandler(void) { return ciHandler; }
- int Slot(void) { return slot; }
+ cMenuSetupCAMItem(cCamSlot *CamSlot);
+ cCamSlot *CamSlot(void) { return camSlot; }
+ bool Changed(void);
};
-cMenuSetupCICAMItem::cMenuSetupCICAMItem(int Device, cCiHandler *CiHandler, int Slot)
+cMenuSetupCAMItem::cMenuSetupCAMItem(cCamSlot *CamSlot)
+{
+ camSlot = CamSlot;
+ SetText("");
+ Changed();
+}
+
+bool cMenuSetupCAMItem::Changed(void)
{
- ciHandler = CiHandler;
- slot = Slot;
char buffer[32];
- const char *CamName = CiHandler->GetCamName(slot);
- snprintf(buffer, sizeof(buffer), "%s%d %d\t%s", tr("Setup.CICAM$CICAM DVB"), Device + 1, slot + 1, CamName ? CamName : "-");
- SetText(buffer);
+ const char *CamName = camSlot->GetCamName();
+ if (!CamName) {
+ switch (camSlot->ModuleStatus()) {
+ case msReset: CamName = tr("CAM reset"); break;
+ case msPresent: CamName = tr("CAM present"); break;
+ case msReady: CamName = tr("CAM ready"); break;
+ default: CamName = "-"; break;
+ }
+ }
+ snprintf(buffer, sizeof(buffer), " %d %s", camSlot->SlotNumber(), CamName);
+ if (strcmp(buffer, Text()) != 0) {
+ SetText(buffer);
+ return true;
+ }
+ return false;
}
-class cMenuSetupCICAM : public cMenuSetupBase {
+class cMenuSetupCAM : public cMenuSetupBase {
private:
eOSState Menu(void);
eOSState Reset(void);
public:
- cMenuSetupCICAM(void);
+ cMenuSetupCAM(void);
virtual eOSState ProcessKey(eKeys Key);
};
-cMenuSetupCICAM::cMenuSetupCICAM(void)
+cMenuSetupCAM::cMenuSetupCAM(void)
{
- SetSection(tr("CICAM"));
- for (int d = 0; d < cDevice::NumDevices(); d++) {
- cDevice *Device = cDevice::GetDevice(d);
- if (Device) {
- cCiHandler *CiHandler = Device->CiHandler();
- if (CiHandler) {
- for (int Slot = 0; Slot < CiHandler->NumSlots(); Slot++)
- Add(new cMenuSetupCICAMItem(Device->CardIndex(), CiHandler, Slot));
- }
- }
- }
+ SetSection(tr("CAM"));
+ SetCols(15);
+ SetHasHotkeys();
+ for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot))
+ Add(new cMenuSetupCAMItem(CamSlot));
SetHelp(tr("Button$Menu"), tr("Button$Reset"));
}
-eOSState cMenuSetupCICAM::Menu(void)
+eOSState cMenuSetupCAM::Menu(void)
{
- cMenuSetupCICAMItem *item = (cMenuSetupCICAMItem *)Get(Current());
+ cMenuSetupCAMItem *item = (cMenuSetupCAMItem *)Get(Current());
if (item) {
- if (item->CiHandler()->EnterMenu(item->Slot())) {
- Skins.Message(mtWarning, tr("Opening CAM menu..."));
- time_t t = time(NULL);
- while (time(NULL) - t < MAXWAITFORCAMMENU && !item->CiHandler()->HasUserIO())
- item->CiHandler()->Process();
- return osEnd; // the CAM menu will be executed explicitly from the main loop
+ if (item->CamSlot()->EnterMenu()) {
+ Skins.Message(mtStatus, tr("Opening CAM menu..."));
+ time_t t0 = time(NULL);
+ time_t t1 = t0;
+ while (time(NULL) - t0 <= MAXWAITFORCAMMENU) {
+ if (item->CamSlot()->HasUserIO())
+ break;
+ if (time(NULL) - t1 >= CAMMENURETYTIMEOUT) {
+ dsyslog("CAM %d: retrying to enter CAM menu...", item->CamSlot()->SlotNumber());
+ item->CamSlot()->EnterMenu();
+ t1 = time(NULL);
+ }
+ cCondWait::SleepMs(100);
+ }
+ Skins.Message(mtStatus, NULL);
+ if (item->CamSlot()->HasUserIO())
+ return AddSubMenu(new cMenuCam(item->CamSlot()));
}
- else
- Skins.Message(mtError, tr("Can't open CAM menu!"));
+ Skins.Message(mtError, tr("Can't open CAM menu!"));
}
return osContinue;
}
-eOSState cMenuSetupCICAM::Reset(void)
+eOSState cMenuSetupCAM::Reset(void)
{
- cMenuSetupCICAMItem *item = (cMenuSetupCICAMItem *)Get(Current());
+ cMenuSetupCAMItem *item = (cMenuSetupCAMItem *)Get(Current());
if (item) {
- Skins.Message(mtWarning, tr("Resetting CAM..."));
- if (item->CiHandler()->Reset(item->Slot())) {
- Skins.Message(mtInfo, tr("CAM has been reset"));
- return osEnd;
+ if (!item->CamSlot()->Device() || Interface->Confirm(tr("CAM is in use - really reset?"))) {
+ if (!item->CamSlot()->Reset())
+ Skins.Message(mtError, tr("Can't reset CAM!"));
}
- else
- Skins.Message(mtError, tr("Can't reset CAM!"));
}
return osContinue;
}
-eOSState cMenuSetupCICAM::ProcessKey(eKeys Key)
+eOSState cMenuSetupCAM::ProcessKey(eKeys Key)
{
- eOSState state = cMenuSetupBase::ProcessKey(Key);
+ eOSState state = HasSubMenu() ? cMenuSetupBase::ProcessKey(Key) : cOsdMenu::ProcessKey(Key);
- if (state == osUnknown) {
+ if (!HasSubMenu()) {
switch (Key) {
+ case kOk:
case kRed: return Menu();
- case kGreen: return Reset();
+ case kGreen: state = Reset(); break;
default: break;
}
+ for (cMenuSetupCAMItem *ci = (cMenuSetupCAMItem *)First(); ci; ci = (cMenuSetupCAMItem *)ci->Next()) {
+ if (ci->Changed())
+ DisplayItem(ci);
+ }
}
return state;
}
@@ -2710,7 +2759,7 @@ void cMenuSetup::Set(void)
Add(new cOsdItem(hk(tr("EPG")), osUser2));
Add(new cOsdItem(hk(tr("DVB")), osUser3));
Add(new cOsdItem(hk(tr("LNB")), osUser4));
- Add(new cOsdItem(hk(tr("CICAM")), osUser5));
+ Add(new cOsdItem(hk(tr("CAM")), osUser5));
Add(new cOsdItem(hk(tr("Recording")), osUser6));
Add(new cOsdItem(hk(tr("Replay")), osUser7));
Add(new cOsdItem(hk(tr("Miscellaneous")), osUser8));
@@ -2740,7 +2789,7 @@ eOSState cMenuSetup::ProcessKey(eKeys Key)
case osUser2: return AddSubMenu(new cMenuSetupEPG);
case osUser3: return AddSubMenu(new cMenuSetupDVB);
case osUser4: return AddSubMenu(new cMenuSetupLNB);
- case osUser5: return AddSubMenu(new cMenuSetupCICAM);
+ case osUser5: return AddSubMenu(new cMenuSetupCAM);
case osUser6: return AddSubMenu(new cMenuSetupRecord);
case osUser7: return AddSubMenu(new cMenuSetupReplay);
case osUser8: return AddSubMenu(new cMenuSetupMisc);
@@ -3126,7 +3175,7 @@ cChannel *cDisplayChannel::NextAvailableChannel(cChannel *Channel, int Direction
if (Direction) {
while (Channel) {
Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(Channel);
- if (Channel && !Channel->GroupSep() && (cDevice::PrimaryDevice()->ProvidesChannel(Channel, Setup.PrimaryLimit) || cDevice::GetDevice(Channel, 0)))
+ if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, 0, true))
return Channel;
}
}
@@ -3541,7 +3590,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
isyslog("record %s", fileName);
if (MakeDirs(fileName, true)) {
const cChannel *ch = timer->Channel();
- recorder = new cRecorder(fileName, ch->Ca(), timer->Priority(), ch->Vpid(), ch->Apids(), ch->Dpids(), ch->Spids());
+ recorder = new cRecorder(fileName, ch->GetChannelID(), timer->Priority(), ch->Vpid(), ch->Apids(), ch->Dpids(), ch->Spids());
if (device->AttachReceiver(recorder)) {
Recording.WriteInfo();
cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true);
@@ -3610,7 +3659,7 @@ void cRecordControl::Stop(void)
bool cRecordControl::Process(time_t t)
{
- if (!recorder || !timer || !timer->Matches(t))
+ if (!recorder || !recorder->IsAttached() || !timer || !timer->Matches(t))
return false;
AssertFreeDiskSpace(timer->Priority());
return true;
@@ -3645,15 +3694,9 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause)
cChannel *channel = Channels.GetByNumber(ch);
if (channel) {
- bool NeedsDetachReceivers = false;
int Priority = Timer ? Timer->Priority() : Pause ? Setup.PausePriority : Setup.DefaultPriority;
- cDevice *device = cDevice::GetDevice(channel, Priority, &NeedsDetachReceivers);
+ cDevice *device = cDevice::GetDevice(channel, Priority, false);
if (device) {
- if (NeedsDetachReceivers) {
- Stop(device);
- if (device == cTransferControl::ReceiverDevice())
- cControl::Shutdown(); // in case this device was used for Transfer Mode
- }
dsyslog("switching device %d to channel %d", device->DeviceNumber() + 1, channel->Number());
if (!device->SwitchChannel(channel, false)) {
cThread::EmergencyExit(true);
@@ -3698,19 +3741,6 @@ void cRecordControls::Stop(const char *InstantId)
}
}
-void cRecordControls::Stop(cDevice *Device)
-{
- ChangeState();
- for (int i = 0; i < MAXRECORDCONTROLS; i++) {
- if (RecordControls[i]) {
- if (RecordControls[i]->Device() == Device) {
- isyslog("stopping recording on DVB device %d due to higher priority", Device->CardIndex() + 1);
- RecordControls[i]->Stop();
- }
- }
- }
-}
-
bool cRecordControls::PauseLiveVideo(void)
{
Skins.Message(mtStatus, tr("Pausing live video..."));
@@ -3882,7 +3912,8 @@ void cReplayControl::Hide(void)
if (visible) {
delete displayReplay;
displayReplay = NULL;
- needsFastResponse = visible = false;
+ SetNeedsFastResponse(false);
+ visible = false;
modeOnly = false;
lastPlay = lastForward = false;
lastSpeed = -2; // an invalid value
@@ -3923,7 +3954,8 @@ bool cReplayControl::ShowProgress(bool Initial)
if (!visible) {
displayReplay = Skins.Current()->DisplayReplay(modeOnly);
displayReplay->SetMarks(&marks);
- needsFastResponse = visible = true;
+ SetNeedsFastResponse(true);
+ visible = true;
}
if (Initial) {
if (title)
diff --git a/menu.h b/menu.h
index 7376b719..b38ce1ff 100644
--- a/menu.h
+++ b/menu.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.h 1.86 2006/10/20 13:09:57 kls Exp $
+ * $Id: menu.h 1.87 2007/01/07 14:40:54 kls Exp $
*/
#ifndef __MENU_H
@@ -128,31 +128,6 @@ public:
eOSState ProcessKey(eKeys Key);
};
-class cMenuCam : public cOsdMenu {
-private:
- cCiMenu *ciMenu;
- bool selected;
- int offset;
- void AddMultiLineItem(const char *s);
- eOSState Select(void);
-public:
- cMenuCam(cCiMenu *CiMenu);
- virtual ~cMenuCam();
- virtual eOSState ProcessKey(eKeys Key);
- };
-
-class cMenuCamEnquiry : public cOsdMenu {
-private:
- cCiEnquiry *ciEnquiry;
- char *input;
- bool replied;
- eOSState Reply(void);
-public:
- cMenuCamEnquiry(cCiEnquiry *CiEnquiry);
- virtual ~cMenuCamEnquiry();
- virtual eOSState ProcessKey(eKeys Key);
- };
-
cOsdObject *CamControl(void);
class cMenuRecordingItem;
@@ -206,7 +181,6 @@ private:
public:
static bool Start(cTimer *Timer = NULL, bool Pause = false);
static void Stop(const char *InstantId);
- static void Stop(cDevice *Device);
static bool PauseLiveVideo(void);
static const char *GetInstantId(const char *LastInstantId);
static cRecordControl *GetRecordControl(const char *FileName);
diff --git a/osdbase.c b/osdbase.c
index 65b66cb8..343ed395 100644
--- a/osdbase.c
+++ b/osdbase.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osdbase.c 1.29 2006/02/05 14:37:03 kls Exp $
+ * $Id: osdbase.c 1.30 2007/01/07 14:41:16 kls Exp $
*/
#include "osdbase.h"
@@ -127,9 +127,9 @@ void cOsdMenu::SetCols(int c0, int c1, int c2, int c3, int c4)
cols[4] = c4;
}
-void cOsdMenu::SetHasHotkeys(void)
+void cOsdMenu::SetHasHotkeys(bool HasHotkeys)
{
- hasHotkeys = true;
+ hasHotkeys = HasHotkeys;
digit = 0;
}
@@ -256,6 +256,20 @@ void cOsdMenu::DisplayCurrent(bool Current)
}
}
+void cOsdMenu::DisplayItem(cOsdItem *Item)
+{
+ if (Item) {
+ int Index = Item->Index();
+ int Offset = Index - first;
+ if (Offset >= 0 && Offset < first + displayMenuItems) {
+ bool Current = Index == current;
+ displayMenu->SetItem(Item->Text(), Offset, Current && Item->Selectable(), Item->Selectable());
+ if (Current && Item->Selectable())
+ cStatus::MsgOsdCurrentItem(Item->Text());
+ }
+ }
+}
+
void cOsdMenu::Clear(void)
{
if (marked >= 0)
@@ -432,6 +446,8 @@ eOSState cOsdMenu::HotKey(eKeys Key)
if (s && (s = skipspace(s)) != NULL) {
if (*s == Key - k1 + '1') {
current = item->Index();
+ RefreshCurrent();
+ Display();
cRemote::Put(kOk, true);
break;
}
@@ -499,4 +515,3 @@ eOSState cOsdMenu::ProcessKey(eKeys Key)
}
return osContinue;
}
-
diff --git a/osdbase.h b/osdbase.h
index 0a94f4a8..07d222d5 100644
--- a/osdbase.h
+++ b/osdbase.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osdbase.h 1.14 2006/01/05 15:35:06 kls Exp $
+ * $Id: osdbase.h 1.15 2007/01/07 14:41:32 kls Exp $
*/
#ifndef __OSDBASE_H
@@ -70,12 +70,13 @@ class cOsdObject {
friend class cOsdMenu;
private:
bool isMenu;
-protected:
bool needsFastResponse;
+protected:
+ void SetNeedsFastResponse(bool NeedsFastResponse) { needsFastResponse = NeedsFastResponse; }
public:
cOsdObject(bool FastResponse = false) { isMenu = false; needsFastResponse = FastResponse; }
virtual ~cOsdObject() {}
- bool NeedsFastResponse(void) { return needsFastResponse; }
+ virtual bool NeedsFastResponse(void) { return needsFastResponse; }
bool IsMenu(void) { return isMenu; }
virtual void Show(void);
virtual eOSState ProcessKey(eKeys Key) { return osUnknown; }
@@ -98,12 +99,13 @@ protected:
cSkinDisplayMenu *DisplayMenu(void) { return displayMenu; }
const char *hk(const char *s);
void SetCols(int c0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0);
- void SetHasHotkeys(void);
+ void SetHasHotkeys(bool HasHotkeys = true);
virtual void Clear(void);
bool SelectableItem(int idx);
void SetCurrent(cOsdItem *Item);
void RefreshCurrent(void);
void DisplayCurrent(bool Current);
+ void DisplayItem(cOsdItem *Item);
void CursorUp(void);
void CursorDown(void);
void PageUp(void);
@@ -120,6 +122,7 @@ protected:
public:
cOsdMenu(const char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0);
virtual ~cOsdMenu();
+ virtual bool NeedsFastResponse(void) { return subMenu ? subMenu->NeedsFastResponse() : cOsdObject::NeedsFastResponse(); }
int Current(void) { return current; }
void Add(cOsdItem *Item, bool Current = false, cOsdItem *After = NULL);
void Ins(cOsdItem *Item, bool Current = false, cOsdItem *Before = NULL);
diff --git a/pat.c b/pat.c
index f898c70d..1f5636b9 100644
--- a/pat.c
+++ b/pat.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: pat.c 1.16 2006/03/31 12:39:34 kls Exp $
+ * $Id: pat.c 1.17 2007/01/07 14:41:55 kls Exp $
*/
#include "pat.h"
@@ -78,7 +78,7 @@ public:
bool Is(cCaDescriptors * CaDescriptors);
bool Empty(void) { return caDescriptors.Count() == 0; }
void AddCaDescriptor(SI::CaDescriptor *d, bool Stream);
- int GetCaDescriptors(const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
+ int GetCaDescriptors(const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
const int *CaIds(void) { return caIds; }
};
@@ -148,7 +148,7 @@ void cCaDescriptors::AddCaDescriptor(SI::CaDescriptor *d, bool Stream)
#endif
}
-int cCaDescriptors::GetCaDescriptors(const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
+int cCaDescriptors::GetCaDescriptors(const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
{
if (!CaSystemIds || !*CaSystemIds)
return 0;
@@ -156,7 +156,7 @@ int cCaDescriptors::GetCaDescriptors(const unsigned short *CaSystemIds, int BufS
int length = 0;
int IsStream = -1;
for (cCaDescriptor *d = caDescriptors.First(); d; d = caDescriptors.Next(d)) {
- const unsigned short *caids = CaSystemIds;
+ const int *caids = CaSystemIds;
do {
if (d->CaSystem() == *caids) {
if (length + d->Length() <= BufSize) {
@@ -187,7 +187,7 @@ public:
// Returns 0 if this is an already known descriptor,
// 1 if it is an all new descriptor with actual contents,
// and 2 if an existing descriptor was changed.
- int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
+ int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
};
int cCaDescriptorHandler::AddCaDescriptors(cCaDescriptors *CaDescriptors)
@@ -208,7 +208,7 @@ int cCaDescriptorHandler::AddCaDescriptors(cCaDescriptors *CaDescriptors)
return CaDescriptors->Empty() ? 0 : 1;
}
-int cCaDescriptorHandler::GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
+int cCaDescriptorHandler::GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
{
cMutexLock MutexLock(&mutex);
StreamFlag = false;
@@ -221,7 +221,7 @@ int cCaDescriptorHandler::GetCaDescriptors(int Source, int Transponder, int Serv
cCaDescriptorHandler CaDescriptorHandler;
-int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
+int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
{
return CaDescriptorHandler.GetCaDescriptors(Source, Transponder, ServiceId, CaSystemIds, BufSize, Data, StreamFlag);
}
diff --git a/pat.h b/pat.h
index 88b2115b..fe337367 100644
--- a/pat.h
+++ b/pat.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: pat.h 1.6 2006/03/29 15:18:38 kls Exp $
+ * $Id: pat.h 1.7 2007/01/07 14:42:11 kls Exp $
*/
#ifndef __PAT_H
@@ -32,7 +32,7 @@ public:
void Trigger(void);
};
-int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
+int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
///< Gets all CA descriptors for a given channel.
///< Copies all available CA descriptors for the given Source, Transponder and ServiceId
///< into the provided buffer at Data (at most BufSize bytes). Only those CA descriptors
diff --git a/receiver.c b/receiver.c
index ddaafefd..b74a18bf 100644
--- a/receiver.c
+++ b/receiver.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: receiver.c 1.5 2006/03/26 14:07:21 kls Exp $
+ * $Id: receiver.c 1.6 2007/01/07 14:42:29 kls Exp $
*/
#include "receiver.h"
@@ -12,10 +12,10 @@
#include <stdio.h>
#include "tools.h"
-cReceiver::cReceiver(int Ca, int Priority, int Pid, const int *Pids1, const int *Pids2, const int *Pids3)
+cReceiver::cReceiver(tChannelID ChannelID, int Priority, int Pid, const int *Pids1, const int *Pids2, const int *Pids3)
{
device = NULL;
- ca = Ca;
+ channelID = ChannelID;
priority = Priority;
numPids = 0;
if (Pid)
diff --git a/receiver.h b/receiver.h
index e1234997..6f136e4b 100644
--- a/receiver.h
+++ b/receiver.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: receiver.h 1.4 2006/05/27 09:04:22 kls Exp $
+ * $Id: receiver.h 1.5 2007/01/07 14:40:36 kls Exp $
*/
#ifndef __RECEIVER_H
@@ -18,7 +18,7 @@ class cReceiver {
friend class cDevice;
private:
cDevice *device;
- int ca;
+ tChannelID channelID;
int priority;
int pids[MAXRECEIVEPIDS];
int numPids;
@@ -38,8 +38,8 @@ protected:
///< will be delivered only ONCE, so the cReceiver must make sure that
///< it will be able to buffer the data if necessary.
public:
- cReceiver(int Ca, int Priority, int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL);
- ///< Creates a new receiver that requires conditional access Ca and has
+ cReceiver(tChannelID ChannelID, int Priority, int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL);
+ ///< Creates a new receiver for the channel with the given ChannelID with
///< the given Priority. Pid is a single PID (typically the video PID), while
///< Pids1...Pids3 are pointers to zero terminated lists of PIDs.
///< If any of these PIDs are 0, they will be silently ignored.
@@ -47,7 +47,18 @@ public:
///< Priority may be any value in the range -99..99. Negative values indicate
///< that this cReceiver may be detached at any time (without blocking the
///< cDevice it is attached to).
+ ///< The ChannelID is necessary to allow the device that will be used for this
+ ///< receiver to detect and store whether the channel can be decrypted in case
+ ///< this is an encrypted channel. If the channel is not encrypted or this
+ ///< detection is not wanted, an invalid tChannelID may be given.
virtual ~cReceiver();
+ tChannelID ChannelID(void) { return channelID; }
+ bool IsAttached(void) { return device != NULL; }
+ ///< Returns true if this receiver is (still) attached to a device.
+ ///< A receiver may be automatically detached from its device in
+ ///< case the device is needed otherwise, so code that uses a cReceiver
+ ///< should repeatedly check whether it is still attached, and if
+ ///< it isn't, delete it (or take any other appropriate measures).
};
#endif //__RECEIVER_H
diff --git a/recorder.c b/recorder.c
index 8bb16212..8c426a5a 100644
--- a/recorder.c
+++ b/recorder.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: recorder.c 1.17 2006/01/08 11:01:25 kls Exp $
+ * $Id: recorder.c 1.18 2007/01/07 14:43:09 kls Exp $
*/
#include "recorder.h"
@@ -21,6 +21,8 @@
#define MINFREEDISKSPACE (512) // MB
#define DISKCHECKINTERVAL 100 // seconds
+// --- cFileWriter -----------------------------------------------------------
+
class cFileWriter : public cThread {
private:
cRemux *remux;
@@ -121,8 +123,10 @@ void cFileWriter::Action(void)
}
}
-cRecorder::cRecorder(const char *FileName, int Ca, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids)
-:cReceiver(Ca, Priority, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
+// --- cRecorder -------------------------------------------------------------
+
+cRecorder::cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids)
+:cReceiver(ChannelID, Priority, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
,cThread("recording")
{
// Make sure the disk is up and running:
diff --git a/recorder.h b/recorder.h
index c4aebbf3..9cff1cc9 100644
--- a/recorder.h
+++ b/recorder.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: recorder.h 1.4 2005/08/13 11:31:18 kls Exp $
+ * $Id: recorder.h 1.5 2007/01/07 14:44:05 kls Exp $
*/
#ifndef __RECORDER_H
@@ -28,9 +28,9 @@ protected:
virtual void Receive(uchar *Data, int Length);
virtual void Action(void);
public:
- cRecorder(const char *FileName, int Ca, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids);
- // Creates a new recorder that requires conditional access Ca, has
- // the given Priority and will record the given PIDs into the file FileName.
+ cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids);
+ // Creates a new recorder for the channel with the given ChannelID and
+ // the given Priority that will record the given PIDs into the file FileName.
virtual ~cRecorder();
};
diff --git a/thread.c b/thread.c
index 2e145e50..05f1d016 100644
--- a/thread.c
+++ b/thread.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: thread.c 1.58 2006/09/24 12:54:47 kls Exp $
+ * $Id: thread.c 1.59 2007/01/07 14:44:22 kls Exp $
*/
#include "thread.h"
@@ -249,17 +249,29 @@ void *cThread::StartThread(cThread *Thread)
return NULL;
}
+#define THREAD_STOP_TIMEOUT 3000 // ms to wait for a thread to stop before newly starting it
+#define THREAD_STOP_SLEEP 30 // ms to sleep while waiting for a thread to stop
+
bool cThread::Start(void)
{
- if (!active) {
- active = running = true;
- if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
- pthread_detach(childTid); // auto-reap
+ if (!running) {
+ if (active) {
+ // Wait until the previous incarnation of this thread has completely ended
+ // before starting it newly:
+ cTimeMs RestartTimeout;
+ while (!running && active && RestartTimeout.Elapsed() < THREAD_STOP_TIMEOUT)
+ cCondWait::SleepMs(THREAD_STOP_SLEEP);
}
- else {
- LOG_ERROR;
- active = running = false;
- return false;
+ if (!active) {
+ active = running = true;
+ if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
+ pthread_detach(childTid); // auto-reap
+ }
+ else {
+ LOG_ERROR;
+ active = running = false;
+ return false;
+ }
}
}
return true;
diff --git a/thread.h b/thread.h
index 6211e2c1..dc45a83b 100644
--- a/thread.h
+++ b/thread.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: thread.h 1.37 2006/09/24 10:10:37 kls Exp $
+ * $Id: thread.h 1.38 2007/01/07 14:44:38 kls Exp $
*/
#ifndef __THREAD_H
@@ -115,6 +115,7 @@ public:
void SetDescription(const char *Description, ...) __attribute__ ((format (printf, 2, 3)));
bool Start(void);
///< Actually starts the thread.
+ ///< If the thread is already running, nothing happens.
bool Active(void);
///< Checks whether the thread is still alive.
static bool EmergencyExit(bool Request = false);
diff --git a/tools.c b/tools.c
index 255c8061..f14de924 100644
--- a/tools.c
+++ b/tools.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: tools.c 1.121 2006/12/02 11:12:59 kls Exp $
+ * $Id: tools.c 1.122 2007/01/07 14:44:57 kls Exp $
*/
#include "tools.h"
@@ -542,9 +542,9 @@ time_t LastModifiedTime(const char *FileName)
// --- cTimeMs ---------------------------------------------------------------
-cTimeMs::cTimeMs(void)
+cTimeMs::cTimeMs(int Ms)
{
- Set();
+ Set(Ms);
}
uint64_t cTimeMs::Now(void)
diff --git a/tools.h b/tools.h
index 1f71ec4b..d671ca9a 100644
--- a/tools.h
+++ b/tools.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: tools.h 1.96 2006/12/03 17:38:38 kls Exp $
+ * $Id: tools.h 1.97 2007/01/07 14:45:11 kls Exp $
*/
#ifndef __TOOLS_H
@@ -23,7 +23,6 @@
#include <sys/types.h>
typedef unsigned char uchar;
-#define uint64 uint64_t // for compatibility - TODO remove in version 1.5
extern int SysLogLevel;
@@ -162,7 +161,8 @@ class cTimeMs {
private:
uint64_t begin;
public:
- cTimeMs(void);
+ cTimeMs(int Ms = 0);
+ ///< Creates a timer with ms resolution and an initial timeout of Ms.
static uint64_t Now(void);
void Set(int Ms = 0);
bool TimedOut(void);
diff --git a/transfer.c b/transfer.c
index afaccd15..cfcb5099 100644
--- a/transfer.c
+++ b/transfer.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: transfer.c 1.33 2006/01/29 17:24:39 kls Exp $
+ * $Id: transfer.c 1.34 2007/01/07 14:45:28 kls Exp $
*/
#include "transfer.h"
@@ -14,8 +14,8 @@
// --- cTransfer -------------------------------------------------------------
-cTransfer::cTransfer(int VPid, const int *APids, const int *DPids, const int *SPids)
-:cReceiver(0, -1, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
+cTransfer::cTransfer(tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids)
+:cReceiver(ChannelID, -1, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
,cThread("transfer")
{
ringBuffer = new cRingBufferLinear(TRANSFERBUFSIZE, TS_SIZE * 2, true, "Transfer");
@@ -34,17 +34,18 @@ void cTransfer::Activate(bool On)
{
if (On)
Start();
- else
+ else {
Cancel(3);
+ cPlayer::Detach();
+ }
}
void cTransfer::Receive(uchar *Data, int Length)
{
- if (IsAttached() && Running()) {
+ if (cPlayer::IsAttached() && Running()) {
int p = ringBuffer->Put(Data, Length);
if (p != Length && Running())
ringBuffer->ReportOverflow(Length - p);
- return;
}
}
@@ -110,8 +111,8 @@ void cTransfer::Action(void)
cDevice *cTransferControl::receiverDevice = NULL;
-cTransferControl::cTransferControl(cDevice *ReceiverDevice, int VPid, const int *APids, const int *DPids, const int *SPids)
-:cControl(transfer = new cTransfer(VPid, APids, DPids, SPids), true)
+cTransferControl::cTransferControl(cDevice *ReceiverDevice, tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids)
+:cControl(transfer = new cTransfer(ChannelID, VPid, APids, DPids, SPids), true)
{
ReceiverDevice->AttachReceiver(transfer);
receiverDevice = ReceiverDevice;
diff --git a/transfer.h b/transfer.h
index 7210ac07..89b94a41 100644
--- a/transfer.h
+++ b/transfer.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: transfer.h 1.11 2006/01/29 17:24:43 kls Exp $
+ * $Id: transfer.h 1.12 2007/01/07 14:45:45 kls Exp $
*/
#ifndef __TRANSFER_H
@@ -25,7 +25,7 @@ protected:
virtual void Receive(uchar *Data, int Length);
virtual void Action(void);
public:
- cTransfer(int VPid, const int *APids, const int *DPids, const int *SPids);
+ cTransfer(tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids);
virtual ~cTransfer();
};
@@ -34,7 +34,7 @@ private:
cTransfer *transfer;
static cDevice *receiverDevice;
public:
- cTransferControl(cDevice *ReceiverDevice, int VPid, const int *APids, const int *DPids, const int *SPids);
+ cTransferControl(cDevice *ReceiverDevice, tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids);
~cTransferControl();
virtual void Hide(void) {}
static cDevice *ReceiverDevice(void) { return receiverDevice; }
diff --git a/vdr.c b/vdr.c
index a628cd01..2416442f 100644
--- a/vdr.c
+++ b/vdr.c
@@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/vdr
*
- * $Id: vdr.c 1.282 2006/12/02 16:22:12 kls Exp $
+ * $Id: vdr.c 1.283 2007/01/07 14:46:14 kls Exp $
*/
#include <getopt.h>
@@ -68,8 +68,6 @@
#define SHUTDOWNWAIT 300 // seconds to wait in user prompt before automatic shutdown
#define MANUALSTART 600 // seconds the next timer must be in the future to assume manual start
#define CHANNELSAVEDELTA 600 // seconds before saving channels.conf after automatic modifications
-#define LASTCAMMENUTIMEOUT 3 // seconds to run the main loop 'fast' after a CAM menu has been closed
- // in order to react on a possible new CAM menu as soon as possible
#define DEVICEREADYTIMEOUT 30 // seconds to wait until all devices are ready
#define MENUTIMEOUT 120 // seconds of user inactivity after which an OSD display is closed
#define SHUTDOWNRETRY 300 // seconds before trying again to shut down
@@ -501,7 +499,6 @@ int main(int argc, char *argv[])
int PreviousChannelIndex = 0;
time_t LastChannelChanged = time(NULL);
time_t LastActivity = 0;
- time_t LastCamMenu = 0;
int MaxLatencyTime = 0;
bool ForceShutdown = false;
bool UserShutdown = false;
@@ -851,19 +848,14 @@ int main(int argc, char *argv[])
DeletedRecordings.Update();
}
// CAM control:
- if (!Menu && !cOsd::IsOpen()) {
+ if (!Menu && !cOsd::IsOpen())
Menu = CamControl();
- if (Menu)
- LastCamMenu = 0;
- else if (!LastCamMenu)
- LastCamMenu = time(NULL);
- }
// Queued messages:
if (!Skins.IsOpen())
Skins.ProcessQueuedMessages();
// User Input:
cOsdObject *Interact = Menu ? Menu : cControl::Control();
- eKeys key = Interface->GetKey((!Interact || !Interact->NeedsFastResponse()) && time(NULL) - LastCamMenu > LASTCAMMENUTIMEOUT);
+ eKeys key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse());
if (NORMALKEY(key) != kNone) {
EITScanner.Activity();
LastActivity = time(NULL);
@@ -1052,9 +1044,6 @@ int main(int argc, char *argv[])
else if (time(NULL) - LastActivity > MENUTIMEOUT)
state = osEnd;
}
- // TODO make the CAM menu stay open in case of automatic updates and have it return osContinue; then the following two lines can be removed again
- else if (state == osEnd && LastActivity > 1)
- LastActivity = time(NULL);
switch (state) {
case osPause: DELETE_MENU;
cControl::Shutdown(); // just in case