summaryrefslogtreecommitdiff
path: root/ci.c
diff options
context:
space:
mode:
Diffstat (limited to 'ci.c')
-rw-r--r--ci.c454
1 files changed, 334 insertions, 120 deletions
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)