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:
parent
2fecf43be9
commit
3a97be4fe9
@ -1249,6 +1249,7 @@ Marco Schl
|
||||
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
|
||||
|
9
HISTORY
9
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.
|
||||
|
454
ci.c
454
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)
|
||||
|
69
ci.h
69
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,25 +66,32 @@ 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);
|
||||
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_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 cCiTransportLayer;
|
||||
class cCiTransportConnection;
|
||||
@ -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);
|
||||
};
|
||||
|
||||
|
34
device.c
34
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);
|
||||
}
|
||||
|
54
dvbdevice.c
54
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:
|
||||
|
Loading…
Reference in New Issue
Block a user