summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKlaus Schmidinger <vdr@tvdr.de>2005-11-26 13:39:47 +0100
committerKlaus Schmidinger <vdr@tvdr.de>2005-11-26 13:39:47 +0100
commit3a97be4fe96792f19674630edac762ae5a060b90 (patch)
treef76dfe9c1b3e5fbd3c9a6369e4b3974a2d353f00
parent2fecf43be9fc7e0372589d1cce408ed5f5e2e210 (diff)
downloadvdr-3a97be4fe96792f19674630edac762ae5a060b90.tar.gz
vdr-3a97be4fe96792f19674630edac762ae5a060b90.tar.bz2
Implemented handling of the "CA PMT Reply" for CAMs; some preparations for being able to record several encrypted channels from the same transponder
-rw-r--r--CONTRIBUTORS1
-rw-r--r--HISTORY9
-rw-r--r--ci.c454
-rw-r--r--ci.h67
-rw-r--r--device.c34
-rw-r--r--dvbdevice.c54
6 files changed, 439 insertions, 180 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index c3b50b17..f2927b0e 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -1249,6 +1249,7 @@ Marco Schlüßler <marco@lordzodiac.de>
for fixing a race condition in the SPU decoder
for fixing initializing the day index when editing the weekday parameter of a
repeating timer
+ for figuring out some obscure length bytes the the CA PMT Reply data of AlphaCrypt CAMs
Jürgen Schmitz <j.schmitz@web.de>
for reporting a bug in displaying the current channel when switching via the SVDRP
diff --git a/HISTORY b/HISTORY
index 8ba1bb23..255cf521 100644
--- a/HISTORY
+++ b/HISTORY
@@ -3934,7 +3934,7 @@ Video Disk Recorder Revision History
layers of subdirectories.
- Removed EPG bugfix #0, because it removed actually important data.
-2005-11-11: Version 1.3.37
+2005-11-26: Version 1.3.37
- Added compiler options "-fPIC -g" to all plugins (thanks to Rolf Ahrenberg).
- Fixed initializing the day index when editing the weekday parameter of a
@@ -3946,3 +3946,10 @@ Video Disk Recorder Revision History
to Werner Fink). Live DD mode requires a full featured DVB card and a
LinuxDVB driver with firmware version 0x2622 or higher. Older versions will
use Transfer Mode just like before.
+- Implemented handling of the "CA PMT Reply" for CAMs (thanks to Marco
+ Schlüßler for figuring out some obscure length bytes in the CA PMT Reply
+ data of AlphaCrypt CAMs).
+- Some preparations for being able to record several encrypted channels from
+ the same transponder at the same time (or record and view different encrypted
+ channels), provided the CAM in use can handle this. This is work in progress
+ and isn't actively used, yet.
diff --git a/ci.c b/ci.c
index 3fe321e6..163ad970 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.39 2005/11/04 14:18:52 kls Exp $
+ * $Id: ci.c 1.40 2005/11/26 13:36:51 kls Exp $
*/
#include "ci.h"
@@ -845,10 +845,118 @@ bool cCiApplicationInformation::EnterMenu(void)
return false;
}
+// --- cCiCaPmt --------------------------------------------------------------
+
+// Ca Pmt List Management:
+
+#define CPLM_MORE 0x00
+#define CPLM_FIRST 0x01
+#define CPLM_LAST 0x02
+#define CPLM_ONLY 0x03
+#define CPLM_ADD 0x04
+#define CPLM_UPDATE 0x05
+
+// Ca Pmt Cmd Ids:
+
+#define CPCI_OK_DESCRAMBLING 0x01
+#define CPCI_OK_MMI 0x02
+#define CPCI_QUERY 0x03
+#define CPCI_NOT_SELECTED 0x04
+
+class cCiCaPmt : public cListObject {
+ friend class cCiConditionalAccessSupport;
+private:
+ uint8_t cmdId;
+ int length;
+ int esInfoLengthPos;
+ uint8_t capmt[2048]; ///< XXX is there a specified maximum?
+ int caDescriptorsLength;
+ uint8_t caDescriptors[2048];
+ 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);
+ void SetListManagement(uint8_t ListManagement);
+ bool Valid(void);
+ void AddPid(int Pid, uint8_t StreamType);
+ };
+
+cCiCaPmt::cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const unsigned short *CaSystemIds)
+{
+ cmdId = CmdId;
+ caDescriptorsLength = GetCaDescriptors(Source, Transponder, ProgramNumber, CaSystemIds, sizeof(caDescriptors), caDescriptors, streamFlag);
+ length = 0;
+ capmt[length++] = CPLM_ONLY;
+ capmt[length++] = (ProgramNumber >> 8) & 0xFF;
+ capmt[length++] = ProgramNumber & 0xFF;
+ capmt[length++] = 0x01; // version_number, current_next_indicator - apparently vn doesn't matter, but cni must be 1
+ esInfoLengthPos = length;
+ capmt[length++] = 0x00; // program_info_length H (at program level)
+ capmt[length++] = 0x00; // program_info_length L
+ if (!streamFlag)
+ AddCaDescriptors(caDescriptorsLength, caDescriptors);
+}
+
+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) {
+ //XXX buffer overflow check???
+ capmt[length++] = StreamType;
+ capmt[length++] = (Pid >> 8) & 0xFF;
+ capmt[length++] = Pid & 0xFF;
+ esInfoLengthPos = length;
+ capmt[length++] = 0x00; // ES_info_length H (at ES level)
+ capmt[length++] = 0x00; // ES_info_length L
+ if (streamFlag)
+ AddCaDescriptors(caDescriptorsLength, caDescriptors);
+ }
+}
+
+void cCiCaPmt::AddCaDescriptors(int Length, const uint8_t *Data)
+{
+ if (esInfoLengthPos) {
+ if (length + Length < int(sizeof(capmt))) {
+ capmt[length++] = cmdId;
+ memcpy(capmt + length, Data, Length);
+ length += Length;
+ int l = length - esInfoLengthPos - 2;
+ capmt[esInfoLengthPos] = (l >> 8) & 0xFF;
+ capmt[esInfoLengthPos + 1] = l & 0xFF;
+ }
+ else
+ esyslog("ERROR: buffer overflow in CA descriptor");
+ esInfoLengthPos = 0;
+ }
+ else
+ esyslog("ERROR: adding CA descriptor without Pid!");
+}
+
// --- cCiConditionalAccessSupport -------------------------------------------
#define MAXCASYSTEMIDS 16
+// CA Enable Ids:
+
+#define CAEI_POSSIBLE 0x01
+#define CAEI_POSSIBLE_COND_PURCHASE 0x02
+#define CAEI_POSSIBLE_COND_TECHNICAL 0x03
+#define CAEI_NOT_POSSIBLE_ENTITLEMENT 0x71
+#define CAEI_NOT_POSSIBLE_TECHNICAL 0x73
+
+#define CA_ENABLE_FLAG 0x80
+
+#define CA_ENABLE(x) (((x) & CA_ENABLE_FLAG) ? (x) & ~CA_ENABLE_FLAG : 0)
+
class cCiConditionalAccessSupport : public cCiSession {
private:
int state;
@@ -858,14 +966,15 @@ public:
cCiConditionalAccessSupport(int 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 SendPMT(cCiCaPmt *CaPmt);
+ bool ReceivedReply(bool CanDescramble = false);
};
cCiConditionalAccessSupport::cCiConditionalAccessSupport(int SessionId, cCiTransportConnection *Tc)
:cCiSession(SessionId, RI_CONDITIONAL_ACCESS_SUPPORT, Tc)
{
dbgprotocol("New Conditional Access Support (session id %d)\n", SessionId);
- state = 0;
+ state = 0; // inactive
caSystemIds[numCaSystemIds = 0] = 0;
}
@@ -892,7 +1001,58 @@ bool cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
}
dbgprotocol("\n");
}
- state = 2;
+ state = 2; // got ca info
+ break;
+ case AOT_CA_PMT_REPLY: {
+ dbgprotocol("%d: <== Ca Pmt Reply", SessionId());
+ state = 4; // 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);
+ dbgprotocol(" %d", pnr);
+ d += 2;
+ l -= 2;
+ if (l > 0) {
+ dbgprotocol(" %02X", *d);
+ d += 1;
+ l -= 1;
+ if (l > 0) {
+ if (l % 3 == 0 && l > 1) {
+ // The EN50221 standard defines that the next byte is supposed
+ // to be the CA_enable value at programme level. However, there are
+ // 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);
+ if (len == l - 2) {
+ d += 2;
+ l -= 2;
+ }
+ }
+ unsigned char caepl = *d;
+ dbgprotocol(" %02X", caepl);
+ d += 1;
+ l -= 1;
+ bool ok = true;
+ if (l <= 2)
+ ok = CA_ENABLE(caepl) == CAEI_POSSIBLE;
+ while (l > 2) {
+ unsigned short pid = ((unsigned short)(*d) << 8) | *(d + 1);
+ unsigned char caees = *(d + 2);
+ dbgprotocol(" %d=%02X", pid, caees);
+ d += 3;
+ l -= 3;
+ if (CA_ENABLE(caees) != CAEI_POSSIBLE)
+ ok = false;
+ }
+ if (ok)
+ state = 5; // descrambling possible
+ }
+ }
+ }
+ dbgprotocol("\n");
+ }
break;
default: esyslog("ERROR: CI conditional access support: unknown tag %06X", Tag);
return false;
@@ -901,20 +1061,27 @@ bool cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
else if (state == 0) {
dbgprotocol("%d: ==> Ca Info Enq\n", SessionId());
SendData(AOT_CA_INFO_ENQ);
- state = 1;
+ state = 1; // enquired ca info
}
return true;
}
-bool cCiConditionalAccessSupport::SendPMT(cCiCaPmt &CaPmt)
+bool cCiConditionalAccessSupport::SendPMT(cCiCaPmt *CaPmt)
{
- if (state == 2) {
- SendData(AOT_CA_PMT, CaPmt.length, CaPmt.capmt);
+ if (CaPmt && state >= 2) {
+ dbgprotocol("%d: ==> Ca Pmt\n", SessionId());
+ SendData(AOT_CA_PMT, CaPmt->length, CaPmt->capmt);
+ state = 3; // sent ca pmt
return true;
}
return false;
}
+bool cCiConditionalAccessSupport::ReceivedReply(bool CanDescramble)
+{
+ return state >= (CanDescramble ? 5 : 4);
+}
+
// --- cCiDateTime -----------------------------------------------------------
class cCiDateTime : public cCiSession {
@@ -1307,78 +1474,6 @@ bool cCiEnquiry::Abort(void)
return mmi && mmi->SendCloseMMI();
}
-// --- cCiCaPmt --------------------------------------------------------------
-
-// Ca Pmt List Management:
-
-#define CPLM_MORE 0x00
-#define CPLM_FIRST 0x01
-#define CPLM_LAST 0x02
-#define CPLM_ONLY 0x03
-#define CPLM_ADD 0x04
-#define CPLM_UPDATE 0x05
-
-// Ca Pmt Cmd Ids:
-
-#define CPCI_OK_DESCRAMBLING 0x01
-#define CPCI_OK_MMI 0x02
-#define CPCI_QUERY 0x03
-#define CPCI_NOT_SELECTED 0x04
-
-cCiCaPmt::cCiCaPmt(int Source, int Transponder, int ProgramNumber, const unsigned short *CaSystemIds)
-{
- caDescriptorsLength = GetCaDescriptors(Source, Transponder, ProgramNumber, CaSystemIds, sizeof(caDescriptors), caDescriptors, streamFlag);
- length = 0;
- capmt[length++] = CPLM_ONLY;
- capmt[length++] = (ProgramNumber >> 8) & 0xFF;
- capmt[length++] = ProgramNumber & 0xFF;
- capmt[length++] = 0x01; // version_number, current_next_indicator - apparently vn doesn't matter, but cni must be 1
- esInfoLengthPos = length;
- capmt[length++] = 0x00; // program_info_length H (at program level)
- capmt[length++] = 0x00; // program_info_length L
- if (!streamFlag)
- AddCaDescriptors(caDescriptorsLength, caDescriptors);
-}
-
-bool cCiCaPmt::Valid(void)
-{
- return caDescriptorsLength > 0;
-}
-
-void cCiCaPmt::AddPid(int Pid, uint8_t StreamType)
-{
- if (Pid) {
- //XXX buffer overflow check???
- capmt[length++] = StreamType;
- capmt[length++] = (Pid >> 8) & 0xFF;
- capmt[length++] = Pid & 0xFF;
- esInfoLengthPos = length;
- capmt[length++] = 0x00; // ES_info_length H (at ES level)
- capmt[length++] = 0x00; // ES_info_length L
- if (streamFlag)
- AddCaDescriptors(caDescriptorsLength, caDescriptors);
- }
-}
-
-void cCiCaPmt::AddCaDescriptors(int Length, const uint8_t *Data)
-{
- if (esInfoLengthPos) {
- if (length + Length < int(sizeof(capmt))) {
- capmt[length++] = CPCI_OK_DESCRAMBLING;
- memcpy(capmt + length, Data, Length);
- length += Length;
- int l = length - esInfoLengthPos - 2;
- capmt[esInfoLengthPos] = (l >> 8) & 0xFF;
- capmt[esInfoLengthPos + 1] = l & 0xFF;
- }
- else
- esyslog("ERROR: buffer overflow in CA descriptor");
- esInfoLengthPos = 0;
- }
- else
- esyslog("ERROR: adding CA descriptor without Pid!");
-}
-
// -- cCiHandler -------------------------------------------------------------
cCiHandler::cCiHandler(int Fd, int NumSlots)
@@ -1393,6 +1488,7 @@ cCiHandler::cCiHandler(int Fd, int NumSlots)
moduleReady[i] = false;
tpl = new cCiTransportLayer(Fd, numSlots);
tc = NULL;
+ source = transponder = 0;
}
cCiHandler::~cCiHandler()
@@ -1556,58 +1652,98 @@ bool cCiHandler::Ready(void)
return true;
}
-bool cCiHandler::Process(void)
+bool cCiHandler::Process(int Slot)
{
bool result = true;
cMutexLock MutexLock(&mutex);
- for (int Slot = 0; Slot < numSlots; 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) {
- int 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);
- }
+ 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) {
+ int 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);
+ }
+ }
+ }
+ 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 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);
}
}
+ SendCaPmt();
bool UserIO = false;
for (int i = 0; i < MAX_CI_SESSION; i++) {
if (sessions[i] && sessions[i]->Process())
UserIO |= sessions[i]->HasUserIO();
}
hasUserIO = UserIO;
- if (newCaSupport)
- newCaSupport = result = false; // triggers new SetCaPmt at caller!
return result;
}
+void cCiHandler::SendCaPmt(void)
+{
+ 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;
+ }
+ }
+ }
+ }
+}
+
bool cCiHandler::EnterMenu(int Slot)
{
cMutexLock MutexLock(&mutex);
@@ -1676,11 +1812,89 @@ bool cCiHandler::ProvidesCa(const unsigned short *CaSystemIds)
return false;
}
-bool cCiHandler::SetCaPmt(cCiCaPmt &CaPmt, int Slot)
+void cCiHandler::SetSource(int Source, int Transponder)
{
cMutexLock MutexLock(&mutex);
- cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT, Slot);
- return cas && cas->SendPMT(CaPmt);
+ if (source != Source || transponder != Transponder) {
+ //XXX if there are active entries, send an empty CA_PMT
+ caProgramList.Clear();
+ }
+ source = Source;
+ transponder = Transponder;
+}
+
+void cCiHandler::AddPid(int ProgramNumber, int Pid, int StreamType)
+{
+ cMutexLock MutexLock(&mutex);
+ cCiCaProgramData *ProgramData = NULL;
+ for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
+ if (p->programNumber == ProgramNumber) {
+ ProgramData = p;
+ for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
+ if (q->pid == Pid)
+ return;
+ }
+ }
+ }
+ if (!ProgramData)
+ caProgramList.Add(ProgramData = new cCiCaProgramData(ProgramNumber));
+ ProgramData->pidList.Add(new cCiCaPidData(Pid, StreamType));
+}
+
+void cCiHandler::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;
+ return;
+ }
+ }
+ }
+}
+
+bool cCiHandler::CanDecrypt(int ProgramNumber)
+{
+ 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;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void cCiHandler::StartDecrypting(void)
+{
+ cMutexLock MutexLock(&mutex);
+ newCaSupport = true;
+ SendCaPmt();
}
bool cCiHandler::Reset(int Slot)
diff --git a/ci.h b/ci.h
index ae760155..f1e289ad 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.18 2005/10/30 12:31:14 kls Exp $
+ * $Id: ci.h 1.19 2005/11/26 13:37:42 kls Exp $
*/
#ifndef __CI_H
@@ -13,6 +13,7 @@
#include <stdint.h>
#include <stdio.h>
#include "thread.h"
+#include "tools.h"
class cCiMMI;
@@ -65,24 +66,31 @@ public:
bool Abort(void);
};
-class cCiCaPmt {
- friend class cCiConditionalAccessSupport;
-private:
- int length;
- int esInfoLengthPos;
- uint8_t capmt[2048]; ///< XXX is there a specified maximum?
- int caDescriptorsLength;
- uint8_t caDescriptors[2048];
- bool streamFlag;
- void AddCaDescriptors(int Length, const uint8_t *Data);
+#define MAX_CI_SESSION 16 //XXX
+#define MAX_CI_SLOT 16
+
+class cCiCaPidData : public cListObject {
public:
- cCiCaPmt(int Source, int Transponder, int ProgramNumber, const unsigned short *CaSystemIds);
- bool Valid(void);
- void AddPid(int Pid, uint8_t StreamType);
+ bool active;
+ int pid;
+ int streamType;
+ cCiCaPidData(int Pid, int StreamType)
+ {
+ active = false;
+ pid = Pid;
+ streamType = StreamType;
+ }
};
-#define MAX_CI_SESSION 16 //XXX
-#define MAX_CI_SLOT 16
+class cCiCaProgramData : public cListObject {
+public:
+ int programNumber;
+ cList<cCiCaPidData> pidList;
+ cCiCaProgramData(int ProgramNumber)
+ {
+ programNumber = ProgramNumber;
+ }
+ };
class cCiSession;
class cCiTransportLayer;
@@ -99,6 +107,9 @@ private:
cCiSession *sessions[MAX_CI_SESSION];
cCiTransportLayer *tpl;
cCiTransportConnection *tc;
+ int source;
+ int transponder;
+ cList<cCiCaProgramData> caProgramList;
int ResourceIdToInt(const uint8_t *Data);
bool Send(uint8_t Tag, int SessionId, int ResourceId = 0, int Status = -1);
cCiSession *GetSessionBySessionId(int SessionId);
@@ -108,12 +119,15 @@ private:
bool CloseSession(int SessionId);
int CloseAllSessions(int Slot);
cCiHandler(int Fd, int NumSlots);
+ void SendCaPmt(void);
public:
~cCiHandler();
static cCiHandler *CreateCiHandler(const char *FileName);
int NumSlots(void) { return numSlots; }
bool Ready(void);
- bool Process(void);
+ 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; }
bool EnterMenu(int Slot);
cCiMenu *GetMenu(void);
@@ -121,7 +135,24 @@ public:
const char *GetCamName(int Slot);
const unsigned short *GetCaSystemIds(int Slot);
bool ProvidesCa(const unsigned short *CaSystemIds); //XXX Slot???
- bool SetCaPmt(cCiCaPmt &CaPmt, int Slot);
+ 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.
+ void SetPid(int Pid, bool Active);
+ ///< Sets the given Pid (which has previously been added through a
+ ///< call to AddPid()) to Active. If Active is true, a later call to
+ ///< StartDecrypting() will send the full list of currently active CA_PMT
+ ///< entries to the CAM, including this one.
+ bool CanDecrypt(int ProgramNumber);
+ ///< XXX
+ void StartDecrypting(void);
+ ///< Triggers sending all currently active CA_PMT entries to the CAM,
+ ///< so that it will start decrypting.
bool Reset(int Slot);
};
diff --git a/device.c b/device.c
index 0da5a492..1b4cec45 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.111 2005/11/05 15:23:58 kls Exp $
+ * $Id: device.c 1.112 2005/11/26 12:56:09 kls Exp $
*/
#include "device.h"
@@ -397,6 +397,8 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
DelPid(Pid, PidType);
return false;
}
+ if (ciHandler)
+ ciHandler->SetPid(Pid, true);
}
PRINTPIDS("a");
return true;
@@ -424,6 +426,8 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
DelPid(Pid, PidType);
return false;
}
+ if (ciHandler)
+ ciHandler->SetPid(Pid, true);
}
}
return true;
@@ -450,6 +454,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);
}
}
PRINTPIDS("E");
@@ -601,12 +607,34 @@ 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
+ // 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() > CACONFBASE) {
+#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 (SetChannelDevice(Channel, LiveView)) {
// Start section handling:
if (sectionHandler) {
sectionHandler->SetChannel(Channel);
sectionHandler->SetStatus(true);
}
+ // Start decrypting any PIDs the might have been set in SetChannelDevice():
+ if (ciHandler)
+ ciHandler->StartDecrypting();
}
else
Result = scrFailed;
@@ -1168,6 +1196,8 @@ bool cDevice::AttachReceiver(cReceiver *Receiver)
Unlock();
if (!Running())
Start();
+ if (ciHandler)
+ ciHandler->StartDecrypting();
return true;
}
}
@@ -1194,6 +1224,8 @@ void cDevice::Detach(cReceiver *Receiver)
else if (receiver[i])
receiversLeft = true;
}
+ if (ciHandler)
+ ciHandler->StartDecrypting();
if (!receiversLeft)
Cancel(3);
}
diff --git a/dvbdevice.c b/dvbdevice.c
index b59076cd..30ce1e4a 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.137 2005/11/11 14:53:52 kls Exp $
+ * $Id: dvbdevice.c 1.138 2005/11/26 13:23:11 kls Exp $
*/
#include "dvbdevice.h"
@@ -35,6 +35,7 @@ extern "C" {
#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"
@@ -69,15 +70,13 @@ static int DvbOpen(const char *Name, int n, int Mode, bool ReportError = false)
class cDvbTuner : public cThread {
private:
- enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked, tsCam };
+ enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked };
int fd_frontend;
int cardIndex;
fe_type_t frontendType;
cCiHandler *ciHandler;
cChannel channel;
const char *diseqcCommands;
- bool useCa;
- time_t startTime;
eTunerStatus tunerStatus;
cMutex mutex;
cCondVar locked;
@@ -89,7 +88,7 @@ public:
cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType, cCiHandler *CiHandler);
virtual ~cDvbTuner();
bool IsTunedTo(const cChannel *Channel) const;
- void Set(const cChannel *Channel, bool Tune, bool UseCa);
+ void Set(const cChannel *Channel, bool Tune);
bool Locked(int TimeoutMs = 0);
};
@@ -100,9 +99,7 @@ cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType, cCi
frontendType = FrontendType;
ciHandler = CiHandler;
diseqcCommands = NULL;
- useCa = false;
tunerStatus = tsIdle;
- startTime = time(NULL);
if (frontendType == FE_QPSK)
CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power
SetDescription("tuner on device %d", cardIndex + 1);
@@ -122,16 +119,11 @@ bool cDvbTuner::IsTunedTo(const cChannel *Channel) const
return tunerStatus != tsIdle && channel.Source() == Channel->Source() && channel.Transponder() == Channel->Transponder();
}
-void cDvbTuner::Set(const cChannel *Channel, bool Tune, bool UseCa)
+void cDvbTuner::Set(const cChannel *Channel, bool Tune)
{
cMutexLock MutexLock(&mutex);
if (Tune)
tunerStatus = tsSet;
- else if (tunerStatus == tsCam)
- tunerStatus = tsLocked;
- useCa = UseCa;
- if (Channel->Ca() && tunerStatus != tsCam)
- startTime = time(NULL);
channel = *Channel;
newSet.Broadcast();
}
@@ -309,7 +301,6 @@ void cDvbTuner::Action(void)
continue;
case tsTuned:
case tsLocked:
- case tsCam:
if (hasEvent) {
if (event.status & FE_REINIT) {
tunerStatus = tsSet;
@@ -323,30 +314,10 @@ void cDvbTuner::Action(void)
}
}
- if (ciHandler) {
- if (ciHandler->Process() && useCa) {
- if (tunerStatus == tsLocked) {
- for (int Slot = 0; Slot < ciHandler->NumSlots(); Slot++) {
- cCiCaPmt CaPmt(channel.Source(), channel.Transponder(), channel.Sid(), ciHandler->GetCaSystemIds(Slot));
- if (CaPmt.Valid()) {
- CaPmt.AddPid(channel.Vpid(), 2);
- CaPmt.AddPid(channel.Apid(0), 4);
- CaPmt.AddPid(channel.Apid(1), 4);
- CaPmt.AddPid(channel.Dpid(0), 0);
- if (ciHandler->SetCaPmt(CaPmt, Slot)) {
- tunerStatus = tsCam;
- startTime = 0;
- }
- }
- }
- }
- }
- else if (tunerStatus > tsLocked)
- tunerStatus = tsLocked;
- }
- // in the beginning we loop more often to let the CAM connection start up fast
+ if (ciHandler)
+ ciHandler->Process();
if (tunerStatus != tsTuned)
- newSet.TimedWait(mutex, (ciHandler && (time(NULL) - startTime < 20)) ? 100 : 1000);
+ newSet.TimedWait(mutex, 1000);
}
}
@@ -782,9 +753,12 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
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() > CACONFBASE || Channel->Ca() > CACONFBASE)
needsDetachReceivers = Ca() != Channel->Ca();
- else if (!IsPrimaryDevice())
+ else
+#endif
+ if (!IsPrimaryDevice())
result = true;
#ifdef DO_REC_AND_PLAY_ON_PRIMARY_DEVICE
else
@@ -834,13 +808,13 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
// Set the tuner:
- dvbTuner->Set(Channel, DoTune, !EITScanner.UsesDevice(this)); //XXX 1.3: this is an ugly hack - find a cleaner solution//XXX
+ dvbTuner->Set(Channel, DoTune);
// If this channel switch was requested by the EITScanner we don't wait for
// a lock and don't set any live PIDs (the EITScanner will wait for the lock
// by itself before setting any filters):
- if (EITScanner.UsesDevice(this))
+ if (EITScanner.UsesDevice(this)) //XXX
return true;
// PID settings: