1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Implemented handling of the "CA PMT Reply" for CAMs; some preparations for being able to record several encrypted channels from the same transponder

This commit is contained in:
Klaus Schmidinger 2005-11-26 13:39:47 +01:00
parent 2fecf43be9
commit 3a97be4fe9
6 changed files with 440 additions and 181 deletions

View File

@ -1249,6 +1249,7 @@ Marco Schl
for fixing a race condition in the SPU decoder for fixing a race condition in the SPU decoder
for fixing initializing the day index when editing the weekday parameter of a for fixing initializing the day index when editing the weekday parameter of a
repeating timer 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> Jürgen Schmitz <j.schmitz@web.de>
for reporting a bug in displaying the current channel when switching via the SVDRP for reporting a bug in displaying the current channel when switching via the SVDRP

View File

@ -3934,7 +3934,7 @@ Video Disk Recorder Revision History
layers of subdirectories. layers of subdirectories.
- Removed EPG bugfix #0, because it removed actually important data. - 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). - Added compiler options "-fPIC -g" to all plugins (thanks to Rolf Ahrenberg).
- Fixed initializing the day index when editing the weekday parameter of a - 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 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 LinuxDVB driver with firmware version 0x2622 or higher. Older versions will
use Transfer Mode just like before. 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.

454
ci.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "ci.h"
@ -845,10 +845,118 @@ bool cCiApplicationInformation::EnterMenu(void)
return false; 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 ------------------------------------------- // --- cCiConditionalAccessSupport -------------------------------------------
#define MAXCASYSTEMIDS 16 #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 { class cCiConditionalAccessSupport : public cCiSession {
private: private:
int state; int state;
@ -858,14 +966,15 @@ public:
cCiConditionalAccessSupport(int SessionId, cCiTransportConnection *Tc); cCiConditionalAccessSupport(int SessionId, cCiTransportConnection *Tc);
virtual bool Process(int Length = 0, const uint8_t *Data = NULL); virtual bool Process(int Length = 0, const uint8_t *Data = NULL);
const unsigned short *GetCaSystemIds(void) { return caSystemIds; } 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) cCiConditionalAccessSupport::cCiConditionalAccessSupport(int SessionId, cCiTransportConnection *Tc)
:cCiSession(SessionId, RI_CONDITIONAL_ACCESS_SUPPORT, Tc) :cCiSession(SessionId, RI_CONDITIONAL_ACCESS_SUPPORT, Tc)
{ {
dbgprotocol("New Conditional Access Support (session id %d)\n", SessionId); dbgprotocol("New Conditional Access Support (session id %d)\n", SessionId);
state = 0; state = 0; // inactive
caSystemIds[numCaSystemIds = 0] = 0; caSystemIds[numCaSystemIds = 0] = 0;
} }
@ -892,7 +1001,58 @@ bool cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
} }
dbgprotocol("\n"); 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; break;
default: esyslog("ERROR: CI conditional access support: unknown tag %06X", Tag); default: esyslog("ERROR: CI conditional access support: unknown tag %06X", Tag);
return false; return false;
@ -901,20 +1061,27 @@ bool cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
else if (state == 0) { else if (state == 0) {
dbgprotocol("%d: ==> Ca Info Enq\n", SessionId()); dbgprotocol("%d: ==> Ca Info Enq\n", SessionId());
SendData(AOT_CA_INFO_ENQ); SendData(AOT_CA_INFO_ENQ);
state = 1; state = 1; // enquired ca info
} }
return true; return true;
} }
bool cCiConditionalAccessSupport::SendPMT(cCiCaPmt &CaPmt) bool cCiConditionalAccessSupport::SendPMT(cCiCaPmt *CaPmt)
{ {
if (state == 2) { if (CaPmt && state >= 2) {
SendData(AOT_CA_PMT, CaPmt.length, CaPmt.capmt); dbgprotocol("%d: ==> Ca Pmt\n", SessionId());
SendData(AOT_CA_PMT, CaPmt->length, CaPmt->capmt);
state = 3; // sent ca pmt
return true; return true;
} }
return false; return false;
} }
bool cCiConditionalAccessSupport::ReceivedReply(bool CanDescramble)
{
return state >= (CanDescramble ? 5 : 4);
}
// --- cCiDateTime ----------------------------------------------------------- // --- cCiDateTime -----------------------------------------------------------
class cCiDateTime : public cCiSession { class cCiDateTime : public cCiSession {
@ -1307,78 +1474,6 @@ bool cCiEnquiry::Abort(void)
return mmi && mmi->SendCloseMMI(); 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::cCiHandler(int Fd, int NumSlots) cCiHandler::cCiHandler(int Fd, int NumSlots)
@ -1393,6 +1488,7 @@ cCiHandler::cCiHandler(int Fd, int NumSlots)
moduleReady[i] = false; moduleReady[i] = false;
tpl = new cCiTransportLayer(Fd, numSlots); tpl = new cCiTransportLayer(Fd, numSlots);
tc = NULL; tc = NULL;
source = transponder = 0;
} }
cCiHandler::~cCiHandler() cCiHandler::~cCiHandler()
@ -1556,58 +1652,98 @@ bool cCiHandler::Ready(void)
return true; return true;
} }
bool cCiHandler::Process(void) bool cCiHandler::Process(int Slot)
{ {
bool result = true; bool result = true;
cMutexLock MutexLock(&mutex); cMutexLock MutexLock(&mutex);
for (int Slot = 0; Slot < numSlots; Slot++) { for (int slot = 0; slot < numSlots; slot++) {
tc = tpl->Process(Slot); if (Slot < 0 || slot == Slot) {
if (tc) { tc = tpl->Process(slot);
int Length; if (tc) {
const uint8_t *Data = tc->Data(Length); int Length;
if (Data && Length > 1) { const uint8_t *Data = tc->Data(Length);
switch (*Data) { if (Data && Length > 1) {
case ST_SESSION_NUMBER: if (Length > 4) { switch (*Data) {
int SessionId = ntohs(get_unaligned((uint16_t *)&Data[2])); case ST_SESSION_NUMBER: if (Length > 4) {
cCiSession *Session = GetSessionBySessionId(SessionId); int SessionId = ntohs(get_unaligned((uint16_t *)&Data[2]));
if (Session) cCiSession *Session = GetSessionBySessionId(SessionId);
Session->Process(Length - 4, Data + 4); if (Session)
else Session->Process(Length - 4, Data + 4);
esyslog("ERROR: unknown session id: %d", SessionId); else
} esyslog("ERROR: unknown session id: %d", SessionId);
break; }
case ST_OPEN_SESSION_REQUEST: OpenSession(Length, Data); break;
break; case ST_OPEN_SESSION_REQUEST: OpenSession(Length, Data);
case ST_CLOSE_SESSION_REQUEST: if (Length == 4) break;
CloseSession(ntohs(get_unaligned((uint16_t *)&Data[2]))); case ST_CLOSE_SESSION_REQUEST: if (Length == 4)
break; CloseSession(ntohs(get_unaligned((uint16_t *)&Data[2])));
case ST_CREATE_SESSION_RESPONSE: //XXX fall through to default break;
case ST_CLOSE_SESSION_RESPONSE: //XXX fall through to default case ST_CREATE_SESSION_RESPONSE: //XXX fall through to default
default: esyslog("ERROR: unknown session tag: %02X", *Data); 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; bool UserIO = false;
for (int i = 0; i < MAX_CI_SESSION; i++) { for (int i = 0; i < MAX_CI_SESSION; i++) {
if (sessions[i] && sessions[i]->Process()) if (sessions[i] && sessions[i]->Process())
UserIO |= sessions[i]->HasUserIO(); UserIO |= sessions[i]->HasUserIO();
} }
hasUserIO = UserIO; hasUserIO = UserIO;
if (newCaSupport)
newCaSupport = result = false; // triggers new SetCaPmt at caller!
return result; 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) bool cCiHandler::EnterMenu(int Slot)
{ {
cMutexLock MutexLock(&mutex); cMutexLock MutexLock(&mutex);
@ -1676,11 +1812,89 @@ bool cCiHandler::ProvidesCa(const unsigned short *CaSystemIds)
return false; return false;
} }
bool cCiHandler::SetCaPmt(cCiCaPmt &CaPmt, int Slot) void cCiHandler::SetSource(int Source, int Transponder)
{ {
cMutexLock MutexLock(&mutex); cMutexLock MutexLock(&mutex);
cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT, Slot); if (source != Source || transponder != Transponder) {
return cas && cas->SendPMT(CaPmt); //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) bool cCiHandler::Reset(int Slot)

69
ci.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __CI_H
@ -13,6 +13,7 @@
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include "thread.h" #include "thread.h"
#include "tools.h"
class cCiMMI; class cCiMMI;
@ -65,25 +66,32 @@ public:
bool Abort(void); 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);
public:
cCiCaPmt(int Source, int Transponder, int ProgramNumber, const unsigned short *CaSystemIds);
bool Valid(void);
void AddPid(int Pid, uint8_t StreamType);
};
#define MAX_CI_SESSION 16 //XXX #define MAX_CI_SESSION 16 //XXX
#define MAX_CI_SLOT 16 #define MAX_CI_SLOT 16
class cCiCaPidData : public cListObject {
public:
bool active;
int pid;
int streamType;
cCiCaPidData(int Pid, int StreamType)
{
active = false;
pid = Pid;
streamType = StreamType;
}
};
class cCiCaProgramData : public cListObject {
public:
int programNumber;
cList<cCiCaPidData> pidList;
cCiCaProgramData(int ProgramNumber)
{
programNumber = ProgramNumber;
}
};
class cCiSession; class cCiSession;
class cCiTransportLayer; class cCiTransportLayer;
class cCiTransportConnection; class cCiTransportConnection;
@ -99,6 +107,9 @@ private:
cCiSession *sessions[MAX_CI_SESSION]; cCiSession *sessions[MAX_CI_SESSION];
cCiTransportLayer *tpl; cCiTransportLayer *tpl;
cCiTransportConnection *tc; cCiTransportConnection *tc;
int source;
int transponder;
cList<cCiCaProgramData> caProgramList;
int ResourceIdToInt(const uint8_t *Data); int ResourceIdToInt(const uint8_t *Data);
bool Send(uint8_t Tag, int SessionId, int ResourceId = 0, int Status = -1); bool Send(uint8_t Tag, int SessionId, int ResourceId = 0, int Status = -1);
cCiSession *GetSessionBySessionId(int SessionId); cCiSession *GetSessionBySessionId(int SessionId);
@ -108,12 +119,15 @@ private:
bool CloseSession(int SessionId); bool CloseSession(int SessionId);
int CloseAllSessions(int Slot); int CloseAllSessions(int Slot);
cCiHandler(int Fd, int NumSlots); cCiHandler(int Fd, int NumSlots);
void SendCaPmt(void);
public: public:
~cCiHandler(); ~cCiHandler();
static cCiHandler *CreateCiHandler(const char *FileName); static cCiHandler *CreateCiHandler(const char *FileName);
int NumSlots(void) { return numSlots; } int NumSlots(void) { return numSlots; }
bool Ready(void); 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 HasUserIO(void) { return hasUserIO; }
bool EnterMenu(int Slot); bool EnterMenu(int Slot);
cCiMenu *GetMenu(void); cCiMenu *GetMenu(void);
@ -121,7 +135,24 @@ public:
const char *GetCamName(int Slot); const char *GetCamName(int Slot);
const unsigned short *GetCaSystemIds(int Slot); const unsigned short *GetCaSystemIds(int Slot);
bool ProvidesCa(const unsigned short *CaSystemIds); //XXX 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); bool Reset(int Slot);
}; };

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "device.h"
@ -397,6 +397,8 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
DelPid(Pid, PidType); DelPid(Pid, PidType);
return false; return false;
} }
if (ciHandler)
ciHandler->SetPid(Pid, true);
} }
PRINTPIDS("a"); PRINTPIDS("a");
return true; return true;
@ -424,6 +426,8 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
DelPid(Pid, PidType); DelPid(Pid, PidType);
return false; return false;
} }
if (ciHandler)
ciHandler->SetPid(Pid, true);
} }
} }
return true; return true;
@ -450,6 +454,8 @@ void cDevice::DelPid(int Pid, ePidType PidType)
if (pidHandles[n].used == 0) { if (pidHandles[n].used == 0) {
pidHandles[n].handle = -1; pidHandles[n].handle = -1;
pidHandles[n].pid = 0; pidHandles[n].pid = 0;
if (ciHandler)
ciHandler->SetPid(Pid, false);
} }
} }
PRINTPIDS("E"); PRINTPIDS("E");
@ -601,12 +607,34 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
sectionHandler->SetStatus(false); sectionHandler->SetStatus(false);
sectionHandler->SetChannel(NULL); 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)) { if (SetChannelDevice(Channel, LiveView)) {
// Start section handling: // Start section handling:
if (sectionHandler) { if (sectionHandler) {
sectionHandler->SetChannel(Channel); sectionHandler->SetChannel(Channel);
sectionHandler->SetStatus(true); sectionHandler->SetStatus(true);
} }
// Start decrypting any PIDs the might have been set in SetChannelDevice():
if (ciHandler)
ciHandler->StartDecrypting();
} }
else else
Result = scrFailed; Result = scrFailed;
@ -1168,6 +1196,8 @@ bool cDevice::AttachReceiver(cReceiver *Receiver)
Unlock(); Unlock();
if (!Running()) if (!Running())
Start(); Start();
if (ciHandler)
ciHandler->StartDecrypting();
return true; return true;
} }
} }
@ -1194,6 +1224,8 @@ void cDevice::Detach(cReceiver *Receiver)
else if (receiver[i]) else if (receiver[i])
receiversLeft = true; receiversLeft = true;
} }
if (ciHandler)
ciHandler->StartDecrypting();
if (!receiversLeft) if (!receiversLeft)
Cancel(3); Cancel(3);
} }

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "dvbdevice.h"
@ -35,6 +35,7 @@ extern "C" {
#define DO_REC_AND_PLAY_ON_PRIMARY_DEVICE 1 #define DO_REC_AND_PLAY_ON_PRIMARY_DEVICE 1
#define DO_MULTIPLE_RECORDINGS 1 #define DO_MULTIPLE_RECORDINGS 1
//#define DO_MULTIPLE_CA_CHANNELS
#define DEV_VIDEO "/dev/video" #define DEV_VIDEO "/dev/video"
#define DEV_DVB_ADAPTER "/dev/dvb/adapter" #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 { class cDvbTuner : public cThread {
private: private:
enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked, tsCam }; enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked };
int fd_frontend; int fd_frontend;
int cardIndex; int cardIndex;
fe_type_t frontendType; fe_type_t frontendType;
cCiHandler *ciHandler; cCiHandler *ciHandler;
cChannel channel; cChannel channel;
const char *diseqcCommands; const char *diseqcCommands;
bool useCa;
time_t startTime;
eTunerStatus tunerStatus; eTunerStatus tunerStatus;
cMutex mutex; cMutex mutex;
cCondVar locked; cCondVar locked;
@ -89,7 +88,7 @@ public:
cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType, cCiHandler *CiHandler); cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType, cCiHandler *CiHandler);
virtual ~cDvbTuner(); virtual ~cDvbTuner();
bool IsTunedTo(const cChannel *Channel) const; 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); bool Locked(int TimeoutMs = 0);
}; };
@ -100,9 +99,7 @@ cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType, cCi
frontendType = FrontendType; frontendType = FrontendType;
ciHandler = CiHandler; ciHandler = CiHandler;
diseqcCommands = NULL; diseqcCommands = NULL;
useCa = false;
tunerStatus = tsIdle; tunerStatus = tsIdle;
startTime = time(NULL);
if (frontendType == FE_QPSK) if (frontendType == FE_QPSK)
CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power
SetDescription("tuner on device %d", cardIndex + 1); 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(); 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); cMutexLock MutexLock(&mutex);
if (Tune) if (Tune)
tunerStatus = tsSet; tunerStatus = tsSet;
else if (tunerStatus == tsCam)
tunerStatus = tsLocked;
useCa = UseCa;
if (Channel->Ca() && tunerStatus != tsCam)
startTime = time(NULL);
channel = *Channel; channel = *Channel;
newSet.Broadcast(); newSet.Broadcast();
} }
@ -309,7 +301,6 @@ void cDvbTuner::Action(void)
continue; continue;
case tsTuned: case tsTuned:
case tsLocked: case tsLocked:
case tsCam:
if (hasEvent) { if (hasEvent) {
if (event.status & FE_REINIT) { if (event.status & FE_REINIT) {
tunerStatus = tsSet; tunerStatus = tsSet;
@ -323,30 +314,10 @@ void cDvbTuner::Action(void)
} }
} }
if (ciHandler) { if (ciHandler)
if (ciHandler->Process() && useCa) { ciHandler->Process();
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 (tunerStatus != tsTuned) 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 (dvbTuner->IsTunedTo(Channel)) {
if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0))) { if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0))) {
#ifdef DO_MULTIPLE_RECORDINGS #ifdef DO_MULTIPLE_RECORDINGS
#ifndef DO_MULTIPLE_CA_CHANNELS
if (Ca() > CACONFBASE || Channel->Ca() > CACONFBASE) if (Ca() > CACONFBASE || Channel->Ca() > CACONFBASE)
needsDetachReceivers = Ca() != Channel->Ca(); needsDetachReceivers = Ca() != Channel->Ca();
else if (!IsPrimaryDevice()) else
#endif
if (!IsPrimaryDevice())
result = true; result = true;
#ifdef DO_REC_AND_PLAY_ON_PRIMARY_DEVICE #ifdef DO_REC_AND_PLAY_ON_PRIMARY_DEVICE
else else
@ -834,13 +808,13 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
// Set the tuner: // 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 // 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 // a lock and don't set any live PIDs (the EITScanner will wait for the lock
// by itself before setting any filters): // by itself before setting any filters):
if (EITScanner.UsesDevice(this)) if (EITScanner.UsesDevice(this)) //XXX
return true; return true;
// PID settings: // PID settings: