summaryrefslogtreecommitdiff
path: root/ci.c
diff options
context:
space:
mode:
authorKlaus Schmidinger <kls (at) cadsoft (dot) de>2005-11-27 18:00:00 +0100
committerKlaus Schmidinger <kls (at) cadsoft (dot) de>2005-11-27 18:00:00 +0100
commit8c63e0fd967a7ac037872ca5af378dc92f0410fa (patch)
tree7f9632af6990a149944b0bb113ee7fac102e99d9 /ci.c
parent812ab9018c7be7feb901eface4c6431b483ca9ec (diff)
downloadvdr-patch-lnbsharing-8c63e0fd967a7ac037872ca5af378dc92f0410fa.tar.gz
vdr-patch-lnbsharing-8c63e0fd967a7ac037872ca5af378dc92f0410fa.tar.bz2
Version 1.3.37vdr-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 repeating timer (thanks to Marco Schlüßler). - No longer removing superfluous hyphens in EPG data - would become too language dependent to handle all kinds of exceptions. - Modified switching to Dolby Digital audio in live mode, if the driver and firmware can handle live DD without the need of a Transfer Mode (thanks 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. - Fixed SetProgress() in the 'skincurses' plugin in case Total is 0 (reported by Stefan Huelswitt). - Added a copy constructor to cString and fixed its assignment operator (thanks to Holger Brunn). - The new function Skins.QueueMessage() can be called from a background thread to queue a message for display. See VDR/skins.h for details. - The SVDRP command MESG uses the new message queueing facility, so MESG commands may now be executed at any time, and the message will be displayed (no more "pending message").
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 3fe321e..163ad97 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)