mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Implemented support for MTD
This commit is contained in:
parent
3f9cdea1c1
commit
2cc25e65f4
6
HISTORY
6
HISTORY
@ -8882,7 +8882,7 @@ Video Disk Recorder Revision History
|
||||
- Added a short sleep to cTSBuffer::Action() to avoid high CPU usage (thanks to
|
||||
Sergey Chernyavskiy).
|
||||
|
||||
2017-02-21: Version 2.3.3
|
||||
2017-03-18: Version 2.3.3
|
||||
|
||||
- Added 'S3W ABS-3A' to sources.conf (thanks to Frank Richter).
|
||||
- Fixed a possible deadlock in the recordings handler thread.
|
||||
@ -8914,3 +8914,7 @@ Video Disk Recorder Revision History
|
||||
contained an empty string).
|
||||
- PIDs can now be added to and deleted from a cReceiver while it is attached to
|
||||
a cDevice, without having to detach it first and re-attach it afterwards.
|
||||
- Implemented support for MTD ("Multi Transponder Decryption"). This allows a CAM
|
||||
that is capable of decrypting more than one channel ("Multi Channel Decryption")
|
||||
to decrypt channels from different transponders. See the remarks in mtd.h on
|
||||
what a derived cCamSlot class needs to do in order to activate MTD.
|
||||
|
4
Makefile
4
Makefile
@ -4,7 +4,7 @@
|
||||
# See the main source file 'vdr.c' for copyright information and
|
||||
# how to reach the author.
|
||||
#
|
||||
# $Id: Makefile 4.2 2017/01/08 11:07:19 kls Exp $
|
||||
# $Id: Makefile 4.3 2017/02/27 16:11:57 kls Exp $
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
@ -69,7 +69,7 @@ SILIB = $(LSIDIR)/libsi.a
|
||||
|
||||
OBJS = args.o audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbci.o\
|
||||
dvbplayer.o dvbspu.o dvbsubtitle.o eit.o eitscan.o epg.o filter.o font.o i18n.o interface.o keys.o\
|
||||
lirc.o menu.o menuitems.o nit.o osdbase.o osd.o pat.o player.o plugin.o positioner.o\
|
||||
lirc.o menu.o menuitems.o mtd.o nit.o osdbase.o osd.o pat.o player.o plugin.o positioner.o\
|
||||
receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o shutdown.o\
|
||||
skinclassic.o skinlcars.o skins.o skinsttng.o sourceparams.o sources.o spu.o status.o svdrp.o themes.o thread.o\
|
||||
timers.o tools.o transfer.o vdr.o videodir.o
|
||||
|
288
ci.c
288
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 4.6 2017/02/21 14:17:07 kls Exp $
|
||||
* $Id: ci.c 4.7 2017/03/18 15:20:27 kls Exp $
|
||||
*/
|
||||
|
||||
#include "ci.h"
|
||||
@ -19,6 +19,7 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include "device.h"
|
||||
#include "mtd.h"
|
||||
#include "pat.h"
|
||||
#include "receiver.h"
|
||||
#include "remux.h"
|
||||
@ -118,6 +119,7 @@ private:
|
||||
cVector<int> emmPids;
|
||||
uchar buffer[2048]; // 11 bit length, max. 2048 byte
|
||||
uchar *bufp;
|
||||
uchar mtdCatBuffer[TS_SIZE]; // TODO: handle multi packet CATs!
|
||||
int length;
|
||||
void AddEmmPid(int Pid);
|
||||
void DelEmmPids(void);
|
||||
@ -157,6 +159,7 @@ void cCaPidReceiver::DelEmmPids(void)
|
||||
void cCaPidReceiver::Receive(const uchar *Data, int Length)
|
||||
{
|
||||
if (TsPid(Data) == CATPID) {
|
||||
cMtdCamSlot *MtdCamSlot = dynamic_cast<cMtdCamSlot *>(Device()->CamSlot());
|
||||
const uchar *p = NULL;
|
||||
if (TsPayloadStart(Data)) {
|
||||
if (Data[5] == SI::TableIdCAT) {
|
||||
@ -166,6 +169,8 @@ void cCaPidReceiver::Receive(const uchar *Data, int Length)
|
||||
if (v != catVersion) {
|
||||
if (Data[11] == 0 && Data[12] == 0) { // section number, last section number
|
||||
if (length > TS_SIZE - 8) {
|
||||
if (MtdCamSlot)
|
||||
esyslog("ERROR: need to implement multi packet CAT handling for MTD!");
|
||||
int n = TS_SIZE - 13;
|
||||
memcpy(buffer, Data + 13, n);
|
||||
bufp = buffer + n;
|
||||
@ -180,6 +185,8 @@ void cCaPidReceiver::Receive(const uchar *Data, int Length)
|
||||
dsyslog("multi table CAT section - unhandled!");
|
||||
catVersion = v;
|
||||
}
|
||||
else if (MtdCamSlot)
|
||||
MtdCamSlot->PutCat(mtdCatBuffer, TS_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -205,12 +212,16 @@ void cCaPidReceiver::Receive(const uchar *Data, int Length)
|
||||
for (int i = 0; i < length - 4; i++) { // -4 = checksum
|
||||
if (p[i] == 0x09) {
|
||||
int CaId = int(p[i + 2] << 8) | p[i + 3];
|
||||
int EmmPid = int(((p[i + 4] & 0x1F) << 8)) | p[i + 5];
|
||||
int EmmPid = Peek13(p + i + 4);
|
||||
AddEmmPid(EmmPid);
|
||||
if (MtdCamSlot)
|
||||
MtdMapPid(const_cast<uchar *>(p + i + 4), MtdCamSlot->MtdMapper());
|
||||
switch (CaId >> 8) {
|
||||
case 0x01: for (int j = i + 7; j < p[i + 1] + 2; j += 4) {
|
||||
EmmPid = (int(p[j] & 0x0F) << 8) | p[j + 1];
|
||||
EmmPid = Peek13(p + j);
|
||||
AddEmmPid(EmmPid);
|
||||
if (MtdCamSlot)
|
||||
MtdMapPid(const_cast<uchar *>(p + j), MtdCamSlot->MtdMapper());
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -220,6 +231,9 @@ void cCaPidReceiver::Receive(const uchar *Data, int Length)
|
||||
p = NULL;
|
||||
bufp = 0;
|
||||
length = 0;
|
||||
memcpy(mtdCatBuffer, Data, TS_SIZE);
|
||||
if (MtdCamSlot)
|
||||
MtdCamSlot->PutCat(mtdCatBuffer, TS_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -266,7 +280,12 @@ void cCaActivationReceiver::Receive(const uchar *Data, int Length)
|
||||
else if (Now - lastScrambledTime > UNSCRAMBLE_TIME) {
|
||||
dsyslog("CAM %d: activated!", camSlot->SlotNumber());
|
||||
Skins.QueueMessage(mtInfo, tr("CAM activated!"));
|
||||
cDevice *d = Device();
|
||||
Detach();
|
||||
if (d) {
|
||||
if (cCamSlot *s = d->CamSlot())
|
||||
s->CancelActivation(); // this will delete *this* object, so no more code referencing *this* after this call!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -757,6 +776,7 @@ public:
|
||||
void SetListManagement(uint8_t ListManagement);
|
||||
uint8_t ListManagement(void) { return capmt.Get(0); }
|
||||
void AddPid(int Pid, uint8_t StreamType);
|
||||
void MtdMapPids(cMtdMapper *MtdMapper);
|
||||
};
|
||||
|
||||
cCiCaPmt::cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds)
|
||||
@ -817,6 +837,84 @@ void cCiCaPmt::AddCaDescriptors(int Length, const uint8_t *Data)
|
||||
esyslog("ERROR: adding CA descriptor without Pid!");
|
||||
}
|
||||
|
||||
static int MtdMapCaDescriptor(uchar *p, cMtdMapper *MtdMapper)
|
||||
{
|
||||
// See pat.c: cCaDescriptor::cCaDescriptor() for the layout of the data!
|
||||
if (*p == SI::CaDescriptorTag) {
|
||||
int l = *++p;
|
||||
if (l >= 4) {
|
||||
MtdMapPid(p + 3, MtdMapper);
|
||||
return l + 2;
|
||||
}
|
||||
else
|
||||
esyslog("ERROR: wrong length (%d) in MtdMapCaDescriptor()", l);
|
||||
}
|
||||
else
|
||||
esyslog("ERROR: wrong tag (%d) in MtdMapCaDescriptor()", *p);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int MtdMapCaDescriptors(uchar *p, cMtdMapper *MtdMapper)
|
||||
{
|
||||
int Length = p[0] * 256 + p[1];
|
||||
if (Length >= 3) {
|
||||
p += 3;
|
||||
int m = Length - 1;
|
||||
while (m > 0) {
|
||||
int l = MtdMapCaDescriptor(p, MtdMapper);
|
||||
if (l > 0) {
|
||||
p += l;
|
||||
m -= l;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Length + 2;
|
||||
}
|
||||
|
||||
static int MtdMapStream(uchar *p, cMtdMapper *MtdMapper)
|
||||
{
|
||||
// See ci.c: cCiCaPmt::AddPid() for the layout of the data!
|
||||
MtdMapPid(p + 1, MtdMapper);
|
||||
int l = MtdMapCaDescriptors(p + 3, MtdMapper);
|
||||
if (l > 0)
|
||||
return l + 3;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int MtdMapStreams(uchar *p, cMtdMapper *MtdMapper, int Length)
|
||||
{
|
||||
int m = Length;
|
||||
while (m >= 5) {
|
||||
int l = MtdMapStream(p, MtdMapper);
|
||||
if (l > 0) {
|
||||
p += l;
|
||||
m -= l;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
return Length;
|
||||
}
|
||||
|
||||
void cCiCaPmt::MtdMapPids(cMtdMapper *MtdMapper)
|
||||
{
|
||||
uchar *p = capmt.Data();
|
||||
int m = capmt.Length();
|
||||
if (m >= 3) {
|
||||
MtdMapSid(p + 1, MtdMapper);
|
||||
p += 4;
|
||||
m -= 4;
|
||||
if (m >= 2) {
|
||||
int l = MtdMapCaDescriptors(p, MtdMapper);
|
||||
if (l >= 0) {
|
||||
p += l;
|
||||
m -= l;
|
||||
MtdMapStreams(p, MtdMapper, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- cCiConditionalAccessSupport -------------------------------------------
|
||||
|
||||
// CA Enable Ids:
|
||||
@ -897,8 +995,12 @@ void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
|
||||
case AOT_CA_PMT_REPLY: {
|
||||
dbgprotocol("Slot %d: <== Ca Pmt Reply (%d)", Tc()->CamSlot()->SlotNumber(), SessionId());
|
||||
if (!repliesToQuery) {
|
||||
dsyslog("CAM %d: replies to QUERY - multi channel decryption possible", Tc()->CamSlot()->SlotNumber());
|
||||
dsyslog("CAM %d: replies to QUERY - multi channel decryption (MCD) possible", Tc()->CamSlot()->SlotNumber());
|
||||
repliesToQuery = true;
|
||||
if (Tc()->CamSlot()->MtdAvailable()) {
|
||||
dsyslog("CAM %d: supports multi transponder decryption (MTD)", Tc()->CamSlot()->SlotNumber());
|
||||
Tc()->CamSlot()->MtdActivate(true);
|
||||
}
|
||||
}
|
||||
state = 5; // got ca pmt reply
|
||||
int l = 0;
|
||||
@ -1650,6 +1752,14 @@ public:
|
||||
programNumber = ProgramNumber;
|
||||
modified = false;
|
||||
}
|
||||
bool Active(void)
|
||||
{
|
||||
for (cCiCaPidData *p = pidList.First(); p; p = pidList.Next(p)) {
|
||||
if (p->active)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// --- cCiAdapter ------------------------------------------------------------
|
||||
@ -1725,15 +1835,18 @@ cCamSlot::cCamSlot(cCiAdapter *CiAdapter, bool WantsTsData, cCamSlot *MasterSlot
|
||||
caPidReceiver = WantsTsData ? new cCaPidReceiver : NULL;
|
||||
caActivationReceiver = NULL;
|
||||
slotIndex = -1;
|
||||
mtdAvailable = false;
|
||||
mtdHandler = NULL;
|
||||
lastModuleStatus = msReset; // avoids initial reset log message
|
||||
resetTime = 0;
|
||||
resendPmt = false;
|
||||
source = transponder = 0;
|
||||
for (int i = 0; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) // tc[0] is not used, but initialized anyway
|
||||
tc[i] = NULL;
|
||||
if (MasterSlot)
|
||||
slotNumber = MasterSlot->SlotNumber();
|
||||
if (ciAdapter) {
|
||||
CamSlots.Add(this);
|
||||
slotNumber = Index() + 1;
|
||||
if (ciAdapter) {
|
||||
ciAdapter->AddCamSlot(this);
|
||||
Reset();
|
||||
}
|
||||
@ -1747,26 +1860,38 @@ cCamSlot::~cCamSlot()
|
||||
delete caActivationReceiver;
|
||||
CamSlots.Del(this, false);
|
||||
DeleteAllConnections();
|
||||
delete mtdHandler;
|
||||
}
|
||||
|
||||
cCamSlot *cCamSlot::MtdSpawn(void)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
if (mtdHandler)
|
||||
return mtdHandler->GetMtdCamSlot(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
bool cCamSlot::Assign(cDevice *Device, bool Query)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
if (Device == assignedDevice)
|
||||
return true;
|
||||
if (ciAdapter) {
|
||||
if (ciAdapter->Assign(Device, true)) {
|
||||
if (!Device && assignedDevice) {
|
||||
int OldDeviceNumber = 0;
|
||||
if (assignedDevice && !Query) {
|
||||
OldDeviceNumber = assignedDevice->DeviceNumber() + 1;
|
||||
if (caPidReceiver)
|
||||
assignedDevice->Detach(caPidReceiver);
|
||||
assignedDevice->SetCamSlot(NULL);
|
||||
assignedDevice = NULL;
|
||||
}
|
||||
if (!Query || !Device) {
|
||||
if (ciAdapter->Assign(Device, true)) {
|
||||
if (!Query) {
|
||||
StopDecrypting();
|
||||
source = transponder = 0;
|
||||
if (ciAdapter->Assign(Device)) {
|
||||
int OldDeviceNumber = assignedDevice ? assignedDevice->DeviceNumber() + 1 : 0;
|
||||
assignedDevice = Device;
|
||||
if (Device) {
|
||||
Device->SetCamSlot(this);
|
||||
assignedDevice = Device;
|
||||
if (caPidReceiver) {
|
||||
caPidReceiver->Reset();
|
||||
Device->AttachReceiver(caPidReceiver);
|
||||
@ -1787,6 +1912,16 @@ bool cCamSlot::Assign(cDevice *Device, bool Query)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cCamSlot::Devices(cVector<int> &CardIndexes)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
if (mtdHandler)
|
||||
return mtdHandler->Devices(CardIndexes);
|
||||
if (assignedDevice)
|
||||
CardIndexes.Append(assignedDevice->CardIndex());
|
||||
return CardIndexes.Size() > 0;
|
||||
}
|
||||
|
||||
void cCamSlot::NewConnection(void)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
@ -1812,8 +1947,6 @@ void cCamSlot::DeleteAllConnections(void)
|
||||
void cCamSlot::Process(cTPDU *TPDU)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
if (caActivationReceiver && !caActivationReceiver->IsAttached())
|
||||
CancelActivation();
|
||||
if (TPDU) {
|
||||
int n = TPDU->Tcid();
|
||||
if (1 <= n && n <= MAX_CONNECTIONS_PER_CAM_SLOT) {
|
||||
@ -1836,6 +1969,7 @@ void cCamSlot::Process(cTPDU *TPDU)
|
||||
case msNone:
|
||||
dbgprotocol("Slot %d: no module present\n", slotNumber);
|
||||
isyslog("CAM %d: no module present", slotNumber);
|
||||
MtdActivate(false);
|
||||
DeleteAllConnections();
|
||||
CancelActivation();
|
||||
break;
|
||||
@ -1852,7 +1986,7 @@ void cCamSlot::Process(cTPDU *TPDU)
|
||||
dbgprotocol("Slot %d: module ready\n", slotNumber);
|
||||
isyslog("CAM %d: module ready", slotNumber);
|
||||
NewConnection();
|
||||
resendPmt = caProgramList.Count() > 0;
|
||||
resendPmt = true;
|
||||
break;
|
||||
default:
|
||||
esyslog("ERROR: unknown module status %d (%s)", ms, __FUNCTION__);
|
||||
@ -1861,8 +1995,14 @@ void cCamSlot::Process(cTPDU *TPDU)
|
||||
}
|
||||
moduleCheckTimer.Set(MODULE_CHECK_INTERVAL);
|
||||
}
|
||||
if (resendPmt)
|
||||
SendCaPmt(CPCI_OK_DESCRAMBLING);
|
||||
if (resendPmt && Ready()) {
|
||||
if (mtdHandler) {
|
||||
mtdHandler->StartDecrypting();
|
||||
resendPmt = false;
|
||||
}
|
||||
else if (caProgramList.Count())
|
||||
StartDecrypting();
|
||||
}
|
||||
processed.Broadcast();
|
||||
}
|
||||
|
||||
@ -1922,12 +2062,18 @@ void cCamSlot::StartActivation(void)
|
||||
void cCamSlot::CancelActivation(void)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
if (mtdHandler)
|
||||
mtdHandler->CancelActivation();
|
||||
else {
|
||||
delete caActivationReceiver;
|
||||
caActivationReceiver = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool cCamSlot::IsActivating(void)
|
||||
{
|
||||
if (mtdHandler)
|
||||
return mtdHandler->IsActivating();
|
||||
return caActivationReceiver;
|
||||
}
|
||||
|
||||
@ -2001,52 +2147,111 @@ cCiEnquiry *cCamSlot::GetEnquiry(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void cCamSlot::SendCaPmt(uint8_t CmdId)
|
||||
cCiCaPmtList::~cCiCaPmtList()
|
||||
{
|
||||
for (int i = 0; i < caPmts.Size(); i++)
|
||||
delete caPmts[i];
|
||||
}
|
||||
|
||||
cCiCaPmt *cCiCaPmtList::Add(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds)
|
||||
{
|
||||
cCiCaPmt *p = new cCiCaPmt(CmdId, Source, Transponder, ProgramNumber, CaSystemIds);
|
||||
caPmts.Append(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
void cCiCaPmtList::Del(cCiCaPmt *CaPmt)
|
||||
{
|
||||
if (caPmts.RemoveElement(CaPmt))
|
||||
delete CaPmt;
|
||||
}
|
||||
|
||||
bool cCamSlot::RepliesToQuery(void)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
|
||||
if (cas) {
|
||||
const int *CaSystemIds = cas->GetCaSystemIds();
|
||||
return cas && cas->RepliesToQuery();
|
||||
}
|
||||
|
||||
void cCamSlot::BuildCaPmts(uint8_t CmdId, cCiCaPmtList &CaPmtList, cMtdMapper *MtdMapper)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
CaPmtList.caPmts.Clear();
|
||||
const int *CaSystemIds = GetCaSystemIds();
|
||||
if (CaSystemIds && *CaSystemIds) {
|
||||
if (caProgramList.Count()) {
|
||||
for (int Loop = 1; Loop <= 2; Loop++) {
|
||||
for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
|
||||
if (p->modified || resendPmt) {
|
||||
bool Active = false;
|
||||
cCiCaPmt CaPmt(CmdId, source, transponder, p->programNumber, CaSystemIds);
|
||||
bool Active = p->Active();
|
||||
cCiCaPmt *CaPmt = CaPmtList.Add(Active ? CmdId : CPCI_NOT_SELECTED, source, transponder, p->programNumber, CaSystemIds);
|
||||
for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
|
||||
if (q->active) {
|
||||
CaPmt.AddPid(q->pid, q->streamType);
|
||||
Active = true;
|
||||
if (q->active)
|
||||
CaPmt->AddPid(q->pid, q->streamType);
|
||||
}
|
||||
}
|
||||
if ((Loop == 1) != Active) { // first remove, then add
|
||||
if (caPidReceiver) {
|
||||
int CaPids[MAXRECEIVEPIDS + 1];
|
||||
if (GetCaPids(source, transponder, p->programNumber, CaSystemIds, MAXRECEIVEPIDS + 1, CaPids) > 0) {
|
||||
if (Loop == 1)
|
||||
caPidReceiver->DelPids(CaPids);
|
||||
else
|
||||
if (Active)
|
||||
caPidReceiver->AddPids(CaPids);
|
||||
else
|
||||
caPidReceiver->DelPids(CaPids);
|
||||
}
|
||||
}
|
||||
if (cas->RepliesToQuery())
|
||||
CaPmt.SetListManagement(Active ? CPLM_ADD : CPLM_UPDATE);
|
||||
if (Active || cas->RepliesToQuery())
|
||||
cas->SendPMT(&CaPmt);
|
||||
if (RepliesToQuery())
|
||||
CaPmt->SetListManagement(Active ? CPLM_ADD : CPLM_UPDATE);
|
||||
if (MtdMapper)
|
||||
CaPmt->MtdMapPids(MtdMapper);
|
||||
p->modified = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cCamSlot::SendCaPmts(cCiCaPmtList &CaPmtList)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
|
||||
if (cas) {
|
||||
for (int i = 0; i < CaPmtList.caPmts.Size(); i++)
|
||||
cas->SendPMT(CaPmtList.caPmts[i]);
|
||||
}
|
||||
resendPmt = false;
|
||||
}
|
||||
else {
|
||||
cCiCaPmt CaPmt(CmdId, 0, 0, 0, NULL);
|
||||
cas->SendPMT(&CaPmt);
|
||||
|
||||
void cCamSlot::SendCaPmt(uint8_t CmdId)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
cCiCaPmtList CaPmtList;
|
||||
BuildCaPmts(CmdId, CaPmtList);
|
||||
SendCaPmts(CaPmtList);
|
||||
}
|
||||
|
||||
void cCamSlot::MtdEnable(void)
|
||||
{
|
||||
mtdAvailable = true;
|
||||
}
|
||||
|
||||
void cCamSlot::MtdActivate(bool On)
|
||||
{
|
||||
if (McdAvailable() && MtdAvailable()) {
|
||||
if (On) {
|
||||
if (!mtdHandler) {
|
||||
dsyslog("CAM %d: activating MTD support", SlotNumber());
|
||||
mtdHandler = new cMtdHandler;
|
||||
}
|
||||
}
|
||||
else if (mtdHandler) {
|
||||
dsyslog("CAM %d: deactivating MTD support", SlotNumber());
|
||||
delete mtdHandler;
|
||||
mtdHandler = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int cCamSlot::MtdPutData(uchar *Data, int Count)
|
||||
{
|
||||
return mtdHandler->Put(Data, Count);
|
||||
}
|
||||
|
||||
const int *cCamSlot::GetCaSystemIds(void)
|
||||
@ -2058,6 +2263,8 @@ const int *cCamSlot::GetCaSystemIds(void)
|
||||
|
||||
int cCamSlot::Priority(void)
|
||||
{
|
||||
if (mtdHandler)
|
||||
return mtdHandler->Priority();
|
||||
cDevice *d = Device();
|
||||
return d ? d->Priority() : IDLEPRIORITY;
|
||||
}
|
||||
@ -2178,15 +2385,14 @@ void cCamSlot::StartDecrypting(void)
|
||||
void cCamSlot::StopDecrypting(void)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
if (caProgramList.Count()) {
|
||||
caProgramList.Clear();
|
||||
SendCaPmt(CPCI_NOT_SELECTED);
|
||||
}
|
||||
}
|
||||
|
||||
bool cCamSlot::IsDecrypting(void)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
if (mtdHandler)
|
||||
return mtdHandler->IsDecrypting();
|
||||
if (caProgramList.Count()) {
|
||||
for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
|
||||
if (p->modified)
|
||||
|
61
ci.h
61
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 4.2 2017/01/23 11:27:39 kls Exp $
|
||||
* $Id: ci.h 4.3 2017/03/18 14:18:37 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __CI_H
|
||||
@ -13,6 +13,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "channels.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "thread.h"
|
||||
#include "tools.h"
|
||||
|
||||
@ -124,10 +125,23 @@ class cCiSession;
|
||||
class cCiCaProgramData;
|
||||
class cCaPidReceiver;
|
||||
class cCaActivationReceiver;
|
||||
class cMtdHandler;
|
||||
class cMtdMapper;
|
||||
class cMtdCamSlot;
|
||||
class cCiCaPmt;
|
||||
|
||||
struct cCiCaPmtList {
|
||||
cVector<cCiCaPmt *> caPmts;
|
||||
~cCiCaPmtList();
|
||||
cCiCaPmt *Add(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds);
|
||||
void Del(cCiCaPmt *CaPmt);
|
||||
};
|
||||
|
||||
class cCamSlot : public cListObject {
|
||||
friend class cCiAdapter;
|
||||
friend class cCiTransportConnection;
|
||||
friend class cCiConditionalAccessSupport;
|
||||
friend class cMtdCamSlot;
|
||||
private:
|
||||
cMutex mutex;
|
||||
cCondVar processed;
|
||||
@ -146,13 +160,40 @@ private:
|
||||
int source;
|
||||
int transponder;
|
||||
cList<cCiCaProgramData> caProgramList;
|
||||
const int *GetCaSystemIds(void);
|
||||
void SendCaPmt(uint8_t CmdId);
|
||||
bool mtdAvailable;
|
||||
cMtdHandler *mtdHandler;
|
||||
void NewConnection(void);
|
||||
void DeleteAllConnections(void);
|
||||
void Process(cTPDU *TPDU = NULL);
|
||||
void Write(cTPDU *TPDU);
|
||||
cCiSession *GetSessionByResourceId(uint32_t ResourceId);
|
||||
void MtdActivate(bool On);
|
||||
///< Activates (On == true) or deactivates (On == false) MTD.
|
||||
protected:
|
||||
virtual const int *GetCaSystemIds(void);
|
||||
virtual void SendCaPmt(uint8_t CmdId);
|
||||
virtual bool RepliesToQuery(void);
|
||||
///< Returns true if the CAM in this slot replies to queries and thus
|
||||
///< supports MCD ("Multi Channel Decryption").
|
||||
void BuildCaPmts(uint8_t CmdId, cCiCaPmtList &CaPmtList, cMtdMapper *MtdMapper = NULL);
|
||||
///< Generates all CA_PMTs with the given CmdId and stores them in the given CaPmtList.
|
||||
///< If MtdMapper is given, all SIDs and PIDs will be mapped accordingly.
|
||||
void SendCaPmts(cCiCaPmtList &CaPmtList);
|
||||
///< Sends the given list of CA_PMTs to the CAM.
|
||||
void MtdEnable(void);
|
||||
///< Enables MTD support for this CAM. Note that actual MTD operation also
|
||||
///< requires a CAM that supports MCD ("Multi Channel Decryption").
|
||||
int MtdPutData(uchar *Data, int Count);
|
||||
///< Sends at most Count bytes of the given Data to the individual MTD CAM slots
|
||||
///< that are using this CAM.
|
||||
///< Returns the number of bytes actually processed.
|
||||
public:
|
||||
bool McdAvailable(void) { return RepliesToQuery(); }
|
||||
///< Returns true if this CAM supports MCD ("Multi Channel Decyption").
|
||||
bool MtdAvailable(void) { return mtdAvailable; }
|
||||
///< Returns true if this CAM supports MTD ("Multi Transponder Decryption").
|
||||
bool MtdActive(void) { return mtdHandler != NULL; }
|
||||
///< Returns true if MTD is currently active.
|
||||
public:
|
||||
cCamSlot(cCiAdapter *CiAdapter, bool WantsTsData = false, cCamSlot *MasterSlot = NULL);
|
||||
///< Creates a new CAM slot for the given CiAdapter.
|
||||
@ -175,6 +216,13 @@ public:
|
||||
cCamSlot *MasterSlot(void) { return masterSlot ? masterSlot : this; }
|
||||
///< Returns this CAM slot's master slot, or a pointer to itself if it is a
|
||||
///< master slot.
|
||||
cCamSlot *MtdSpawn(void);
|
||||
///< If this CAM slot can do MTD ("Multi Transponder Decryption"),
|
||||
///< a call to this function returns a cMtdCamSlot with this CAM slot
|
||||
///< as its master. Otherwise a pointer to this object is returned, which
|
||||
///< means that MTD is not supported.
|
||||
void TriggerResendPmt(void) { resendPmt = true; }
|
||||
///< Tells this CAM slot to resend the list of CA_PMTs to the CAM.
|
||||
virtual bool Assign(cDevice *Device, bool Query = false);
|
||||
///< Assigns this CAM slot to the given Device, if this is possible.
|
||||
///< If Query is 'true', the CI adapter of this slot only checks whether
|
||||
@ -190,6 +238,10 @@ public:
|
||||
///< class function.
|
||||
cDevice *Device(void) { return assignedDevice; }
|
||||
///< Returns the device this CAM slot is currently assigned to.
|
||||
bool Devices(cVector<int> &CardIndexes);
|
||||
///< Adds the card indexes of any devices that currently use this CAM to
|
||||
///< the given CardIndexes. This can be more than one in case of MTD.
|
||||
///< Returns true if the array is not empty.
|
||||
bool WantsTsData(void) const { return caPidReceiver != NULL; }
|
||||
///< Returns true if this CAM slot wants to receive the TS data through
|
||||
///< its Decrypt() function.
|
||||
@ -308,7 +360,8 @@ public:
|
||||
///< the CAM's control). If no decrypted TS packet is currently available, NULL
|
||||
///< shall be returned. If no data from Data can currently be processed, Count
|
||||
///< shall be set to 0 and the same Data pointer will be offered in the next
|
||||
///< call to Decrypt().
|
||||
///< call to Decrypt(). See mtd.h for further requirements if this CAM can
|
||||
///< do MTD ("Multi Transponder Decryption").
|
||||
///< A derived class that implements this function will also need
|
||||
///< to set the WantsTsData parameter in the call to the base class
|
||||
///< constructor to true in order to receive the TS data.
|
||||
|
76
device.c
76
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 4.7 2017/02/21 13:38:01 kls Exp $
|
||||
* $Id: device.c 4.8 2017/03/18 15:45:53 kls Exp $
|
||||
*/
|
||||
|
||||
#include "device.h"
|
||||
@ -281,8 +281,13 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView
|
||||
continue; // CAM slot can't be used with this device
|
||||
bool ndr;
|
||||
if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basically able to do the job
|
||||
if (NumUsableSlots && !HasInternalCam && device[i]->CamSlot() && device[i]->CamSlot() != CamSlots.Get(j))
|
||||
if (NumUsableSlots && !HasInternalCam) {
|
||||
if (cCamSlot *csi = device[i]->CamSlot()) {
|
||||
cCamSlot *csj = CamSlots.Get(j);
|
||||
if ((csj->MtdActive() ? csi->MasterSlot() : csi) != csj)
|
||||
ndr = true; // using a different CAM slot requires detaching receivers
|
||||
}
|
||||
}
|
||||
// Put together an integer number that reflects the "impact" using
|
||||
// this device would have on the overall system. Each condition is represented
|
||||
// by one bit in the number (or several bits, if the condition is actually
|
||||
@ -319,13 +324,76 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView
|
||||
if (NeedsDetachReceivers)
|
||||
d->DetachAllReceivers();
|
||||
if (s) {
|
||||
// Some of the following statements could probably be combined, but let's keep them
|
||||
// explicit so we can clearly see every single aspect of the decisions made here.
|
||||
if (d->CamSlot()) {
|
||||
if (s->MtdActive()) {
|
||||
if (s == d->CamSlot()->MasterSlot()) {
|
||||
// device d already has a proper CAM slot, so nothing to do here
|
||||
}
|
||||
else {
|
||||
// device d has a CAM slot, but it's not the right one
|
||||
d->CamSlot()->Assign(NULL);
|
||||
s = s->MtdSpawn();
|
||||
s->Assign(d);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (s->Device()) {
|
||||
if (s->Device() != d) {
|
||||
if (s->Device())
|
||||
// CAM slot s is currently assigned to a different device than d
|
||||
if (Priority > s->Priority()) {
|
||||
s->Device()->DetachAllReceivers();
|
||||
if (d->CamSlot())
|
||||
d->CamSlot()->Assign(NULL);
|
||||
s->Assign(d);
|
||||
}
|
||||
else {
|
||||
d = NULL;
|
||||
s = NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// device d already has a proper CAM slot, so nothing to do here
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (s != d->CamSlot()) {
|
||||
// device d has a CAM slot, but it's not the right one
|
||||
d->CamSlot()->Assign(NULL);
|
||||
s->Assign(d);
|
||||
}
|
||||
else {
|
||||
// device d already has a proper CAM slot, so nothing to do here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// device d has no CAM slot, ...
|
||||
if (s->MtdActive()) {
|
||||
// ... so we assign s with MTD support
|
||||
s = s->MtdSpawn();
|
||||
s->Assign(d);
|
||||
}
|
||||
else {
|
||||
// CAM slot s has no MTD support ...
|
||||
if (s->Device()) {
|
||||
// ... but it is assigned to a different device, so we reassign it to d
|
||||
if (Priority > s->Priority()) {
|
||||
s->Device()->DetachAllReceivers();
|
||||
s->Assign(d);
|
||||
}
|
||||
else {
|
||||
d = NULL;
|
||||
s = NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// ... and is not assigned to any device, so we just assign it to d
|
||||
s->Assign(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (d->CamSlot() && !d->CamSlot()->IsDecrypting())
|
||||
d->CamSlot()->Assign(NULL);
|
||||
|
20
menu.c
20
menu.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menu.c 4.21 2017/01/23 12:01:48 kls Exp $
|
||||
* $Id: menu.c 4.22 2017/03/18 14:27:50 kls Exp $
|
||||
*/
|
||||
|
||||
#include "menu.h"
|
||||
@ -3770,15 +3770,18 @@ bool cMenuSetupCAMItem::Changed(void)
|
||||
else if (camSlot->IsActivating())
|
||||
// TRANSLATORS: note the leading blank!
|
||||
Activating = tr(" (activating)");
|
||||
cVector<int> CardIndexes;
|
||||
for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
|
||||
if (CamSlot == camSlot || CamSlot->MasterSlot() == camSlot) {
|
||||
if (cDevice *Device = CamSlot->Device()) {
|
||||
if (!**AssignedDevice)
|
||||
if (CamSlot == camSlot || CamSlot->MasterSlot() == camSlot)
|
||||
CamSlot->Devices(CardIndexes);
|
||||
}
|
||||
if (CardIndexes.Size() > 0) {
|
||||
AssignedDevice = cString::sprintf(" %s", tr("@ device"));
|
||||
AssignedDevice = cString::sprintf("%s %d", *AssignedDevice, Device->CardIndex() + 1);
|
||||
}
|
||||
}
|
||||
CardIndexes.Sort(CompareInts);
|
||||
for (int i = 0; i < CardIndexes.Size(); i++)
|
||||
AssignedDevice = cString::sprintf("%s %d", *AssignedDevice, CardIndexes[i] + 1);
|
||||
}
|
||||
|
||||
cString buffer = cString::sprintf(" %d %s%s%s", camSlot->SlotNumber(), CamName, *AssignedDevice, Activating);
|
||||
if (strcmp(buffer, Text()) != 0) {
|
||||
SetText(buffer);
|
||||
@ -3874,9 +3877,9 @@ eOSState cMenuSetupCAM::Activate(void)
|
||||
if (cDevice *Device = cDevice::GetDevice(i)) {
|
||||
if (Device->ProvidesChannel(Channel)) {
|
||||
if (Device->Priority() < LIVEPRIORITY) { // don't interrupt recordings
|
||||
if (CamSlot->CanActivate()) {
|
||||
if (CamSlot->Assign(Device, true)) { // query
|
||||
cControl::Shutdown(); // must end transfer mode before assigning CAM, otherwise it might be unassigned again
|
||||
CamSlot = CamSlot->MtdSpawn();
|
||||
if (CamSlot->Assign(Device)) {
|
||||
if (Device->SwitchChannel(Channel, true)) {
|
||||
CamSlot->StartActivation();
|
||||
@ -3890,7 +3893,6 @@ eOSState cMenuSetupCAM::Activate(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Skins.Message(mtError, tr("Can't activate CAM!"));
|
||||
}
|
||||
}
|
||||
|
323
mtd.c
Normal file
323
mtd.c
Normal file
@ -0,0 +1,323 @@
|
||||
/*
|
||||
* mtd.c: Multi Transponder Decryption
|
||||
*
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: mtd.c 1.1 2017/03/18 14:31:34 kls Exp $
|
||||
*/
|
||||
|
||||
#include "mtd.h"
|
||||
#include "receiver.h"
|
||||
|
||||
//#define DEBUG_MTD
|
||||
#ifdef DEBUG_MTD
|
||||
#define DBGMTD(a...) dsyslog(a)
|
||||
#else
|
||||
#define DBGMTD(a...)
|
||||
#endif
|
||||
|
||||
//#define KEEPPIDS // for testing and debugging - USE ONLY IF YOU KNOW WHAT YOU ARE DOING!
|
||||
|
||||
#define MAX_REAL_PIDS MAXPID // real PIDs are 13 bit (0x0000 - 0x1FFF)
|
||||
#ifdef KEEPPIDS
|
||||
#define MAX_UNIQ_PIDS MAXPID
|
||||
#define UNIQ_PID_MASK 0x1FFF
|
||||
#else
|
||||
#define MAX_UNIQ_PIDS 256 // uniq PIDs are 8 bit (0x00 - 0xFF)
|
||||
#define UNIQ_PID_MASK 0x00FF
|
||||
#define UNIQ_PID_SHIFT 8
|
||||
#endif // KEEPPIDS
|
||||
|
||||
// --- cMtdHandler -----------------------------------------------------------
|
||||
|
||||
cMtdHandler::cMtdHandler(void)
|
||||
{
|
||||
}
|
||||
|
||||
cMtdHandler::~cMtdHandler()
|
||||
{
|
||||
for (int i = 0; i < camSlots.Size(); i++) {
|
||||
dsyslog("CAM %d/%d: deleting MTD CAM slot", camSlots[i]->MasterSlot()->SlotNumber(), i + 1);
|
||||
delete camSlots[i];
|
||||
}
|
||||
}
|
||||
|
||||
cMtdCamSlot *cMtdHandler::GetMtdCamSlot(cCamSlot *MasterSlot)
|
||||
{
|
||||
for (int i = 0; i < camSlots.Size(); i++) {
|
||||
if (!camSlots[i]->Device()) {
|
||||
dsyslog("CAM %d/%d: reusing MTD CAM slot", MasterSlot->SlotNumber(), i + 1);
|
||||
return camSlots[i];
|
||||
}
|
||||
}
|
||||
dsyslog("CAM %d/%d: creating new MTD CAM slot", MasterSlot->SlotNumber(), camSlots.Size() + 1);
|
||||
cMtdCamSlot *s = new cMtdCamSlot(MasterSlot, camSlots.Size());
|
||||
camSlots.Append(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
int cMtdHandler::Put(const uchar *Data, int Count)
|
||||
{
|
||||
// TODO maybe handle more than one TS packet?
|
||||
if (Count > TS_SIZE)
|
||||
Count = TS_SIZE;
|
||||
else if (Count < TS_SIZE)
|
||||
return 0;
|
||||
int Pid = TsPid(Data);
|
||||
if (Pid == CATPID)
|
||||
return Count; // this is the original CAT with mapped PIDs
|
||||
#ifdef KEEPPIDS
|
||||
int Index = 0;
|
||||
#else
|
||||
int Index = (Pid >> UNIQ_PID_SHIFT) - 1;
|
||||
#endif // KEEPPIDS
|
||||
if (Index >= 0 && Index < camSlots.Size())
|
||||
return camSlots[Index]->PutData(Data, Count);
|
||||
else
|
||||
esyslog("ERROR: invalid MTD number (%d) in PID %d (%04X)", Index + 1, Pid, Pid);
|
||||
return Count; // no such buffer - let's just drop the data so nothing stacks up
|
||||
}
|
||||
|
||||
int cMtdHandler::Priority(void)
|
||||
{
|
||||
int p = IDLEPRIORITY;
|
||||
for (int i = 0; i < camSlots.Size(); i++)
|
||||
p = max(p, camSlots[i]->Priority());
|
||||
return p;
|
||||
}
|
||||
|
||||
bool cMtdHandler::IsDecrypting(void)
|
||||
{
|
||||
for (int i = 0; i < camSlots.Size(); i++) {
|
||||
if (camSlots[i]->IsDecrypting())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cMtdHandler::StartDecrypting(void)
|
||||
{
|
||||
for (int i = 0; i < camSlots.Size(); i++) {
|
||||
if (camSlots[i]->Device()) {
|
||||
camSlots[i]->TriggerResendPmt();
|
||||
camSlots[i]->StartDecrypting();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cMtdHandler::CancelActivation(void)
|
||||
{
|
||||
for (int i = 0; i < camSlots.Size(); i++)
|
||||
camSlots[i]->CancelActivation();
|
||||
}
|
||||
|
||||
bool cMtdHandler::IsActivating(void)
|
||||
{
|
||||
for (int i = 0; i < camSlots.Size(); i++) {
|
||||
if (camSlots[i]->IsActivating())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cMtdHandler::Devices(cVector<int> &CardIndexes)
|
||||
{
|
||||
for (int i = 0; i < camSlots.Size(); i++)
|
||||
camSlots[i]->Devices(CardIndexes);
|
||||
return CardIndexes.Size() > 0;
|
||||
}
|
||||
|
||||
// --- cMtdMapper ------------------------------------------------------------
|
||||
|
||||
#define MTD_INVALID_PID 0xFFFF
|
||||
|
||||
class cMtdMapper {
|
||||
private:
|
||||
int number;
|
||||
int masterCamSlotNumber;
|
||||
uint16_t uniqPids[MAX_REAL_PIDS]; // maps a real PID to a unique PID
|
||||
uint16_t realPids[MAX_UNIQ_PIDS]; // maps a unique PID to a real PID
|
||||
cVector<uint16_t> uniqSids;
|
||||
uint16_t MakeUniqPid(uint16_t RealPid);
|
||||
public:
|
||||
cMtdMapper(int Number, int MasterCamSlotNumber);
|
||||
~cMtdMapper();
|
||||
uint16_t RealToUniqPid(uint16_t RealPid) { if (uniqPids[RealPid]) return uniqPids[RealPid]; return MakeUniqPid(RealPid); }
|
||||
uint16_t UniqToRealPid(uint16_t UniqPid) { return realPids[UniqPid & UNIQ_PID_MASK]; }
|
||||
uint16_t RealToUniqSid(uint16_t RealSid);
|
||||
void Clear(void);
|
||||
};
|
||||
|
||||
cMtdMapper::cMtdMapper(int Number, int MasterCamSlotNumber)
|
||||
{
|
||||
number = Number;
|
||||
masterCamSlotNumber = MasterCamSlotNumber;
|
||||
Clear();
|
||||
}
|
||||
|
||||
cMtdMapper::~cMtdMapper()
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t cMtdMapper::MakeUniqPid(uint16_t RealPid)
|
||||
{
|
||||
#ifdef KEEPPIDS
|
||||
uniqPids[RealPid] = realPids[RealPid] = RealPid;
|
||||
DBGMTD("CAM %d/%d: mapped PID %d (%04X) to %d (%04X)", masterCamSlotNumber, number, RealPid, RealPid, uniqPids[RealPid], uniqPids[RealPid]);
|
||||
return uniqPids[RealPid];
|
||||
#else
|
||||
for (int i = 0; i < MAX_UNIQ_PIDS; i++) {
|
||||
if (realPids[i] == MTD_INVALID_PID) { // 0x0000 is a valid PID (PAT)!
|
||||
realPids[i] = RealPid;
|
||||
uniqPids[RealPid] = (number << UNIQ_PID_SHIFT) | i;
|
||||
DBGMTD("CAM %d/%d: mapped PID %d (%04X) to %d (%04X)", masterCamSlotNumber, number, RealPid, RealPid, uniqPids[RealPid], uniqPids[RealPid]);
|
||||
return uniqPids[RealPid];
|
||||
}
|
||||
}
|
||||
#endif // KEEPPIDS
|
||||
esyslog("ERROR: MTD %d: mapper ran out of unique PIDs", number);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t cMtdMapper::RealToUniqSid(uint16_t RealSid)
|
||||
{
|
||||
#ifdef KEEPPIDS
|
||||
return RealSid;
|
||||
#endif // KEEPPIDS
|
||||
int UniqSid = uniqSids.IndexOf(RealSid);
|
||||
if (UniqSid < 0) {
|
||||
UniqSid = uniqSids.Size();
|
||||
uniqSids.Append(RealSid);
|
||||
DBGMTD("CAM %d/%d: mapped SID %d (%04X) to %d (%04X)", masterCamSlotNumber, number, RealSid, RealSid, UniqSid | (number << UNIQ_PID_SHIFT), UniqSid | (number << UNIQ_PID_SHIFT));
|
||||
}
|
||||
UniqSid |= number << UNIQ_PID_SHIFT;
|
||||
return UniqSid;
|
||||
}
|
||||
|
||||
void cMtdMapper::Clear(void)
|
||||
{
|
||||
DBGMTD("CAM %d/%d: MTD mapper cleared", masterCamSlotNumber, number);
|
||||
memset(uniqPids, 0, sizeof(uniqPids));
|
||||
memset(realPids, MTD_INVALID_PID, sizeof(realPids));
|
||||
uniqSids.Clear();
|
||||
}
|
||||
|
||||
void MtdMapSid(uchar *p, cMtdMapper *MtdMapper)
|
||||
{
|
||||
Poke13(p, MtdMapper->RealToUniqSid(Peek13(p)));
|
||||
}
|
||||
|
||||
void MtdMapPid(uchar *p, cMtdMapper *MtdMapper)
|
||||
{
|
||||
Poke13(p, MtdMapper->RealToUniqPid(Peek13(p)));
|
||||
}
|
||||
|
||||
// --- cMtdCamSlot -----------------------------------------------------------
|
||||
|
||||
#define MTD_BUFFER_SIZE MEGABYTE(1)
|
||||
|
||||
cMtdCamSlot::cMtdCamSlot(cCamSlot *MasterSlot, int Index)
|
||||
:cCamSlot(NULL, true, MasterSlot)
|
||||
{
|
||||
mtdBuffer = new cRingBufferLinear(MTD_BUFFER_SIZE, TS_SIZE, true, "MTD buffer");
|
||||
mtdMapper = new cMtdMapper(Index + 1, MasterSlot->SlotNumber());
|
||||
delivered = false;
|
||||
ciAdapter = MasterSlot->ciAdapter; // we don't pass the CI adapter in the constructor, to prevent this one from being inserted into CamSlots
|
||||
}
|
||||
|
||||
cMtdCamSlot::~cMtdCamSlot()
|
||||
{
|
||||
delete mtdMapper;
|
||||
delete mtdBuffer;
|
||||
}
|
||||
|
||||
const int *cMtdCamSlot::GetCaSystemIds(void)
|
||||
{
|
||||
return MasterSlot()->GetCaSystemIds();
|
||||
}
|
||||
|
||||
void cMtdCamSlot::SendCaPmt(uint8_t CmdId)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
cCiCaPmtList CaPmtList;
|
||||
BuildCaPmts(CmdId, CaPmtList, mtdMapper);
|
||||
MasterSlot()->SendCaPmts(CaPmtList);
|
||||
}
|
||||
|
||||
bool cMtdCamSlot::RepliesToQuery(void)
|
||||
{
|
||||
return MasterSlot()->RepliesToQuery();
|
||||
}
|
||||
|
||||
bool cMtdCamSlot::ProvidesCa(const int *CaSystemIds)
|
||||
{
|
||||
return MasterSlot()->ProvidesCa(CaSystemIds);
|
||||
}
|
||||
|
||||
bool cMtdCamSlot::CanDecrypt(const cChannel *Channel)
|
||||
{
|
||||
//TODO PID mapping?
|
||||
return MasterSlot()->CanDecrypt(Channel);
|
||||
}
|
||||
|
||||
void cMtdCamSlot::StartDecrypting(void)
|
||||
{
|
||||
MasterSlot()->StartDecrypting();
|
||||
cCamSlot::StartDecrypting();
|
||||
}
|
||||
|
||||
void cMtdCamSlot::StopDecrypting(void)
|
||||
{
|
||||
cCamSlot::StopDecrypting();
|
||||
mtdMapper->Clear();
|
||||
//XXX mtdBuffer->Clear(); //XXX would require locking?
|
||||
}
|
||||
|
||||
bool cMtdCamSlot::IsDecrypting(void)
|
||||
{
|
||||
return cCamSlot::IsDecrypting();
|
||||
}
|
||||
|
||||
uchar *cMtdCamSlot::Decrypt(uchar *Data, int &Count)
|
||||
{
|
||||
// Send data to CAM:
|
||||
if (Count >= TS_SIZE) {
|
||||
Count = TS_SIZE;
|
||||
int Pid = TsPid(Data);
|
||||
TsSetPid(Data, mtdMapper->RealToUniqPid(Pid));
|
||||
MasterSlot()->Decrypt(Data, Count);
|
||||
if (Count == 0)
|
||||
TsSetPid(Data, Pid); // must restore PID for later retry
|
||||
}
|
||||
else
|
||||
Count = 0;
|
||||
// Drop delivered data from previous call:
|
||||
if (delivered) {
|
||||
mtdBuffer->Del(TS_SIZE);
|
||||
delivered = false;
|
||||
}
|
||||
// Receive data from buffer:
|
||||
int c = 0;
|
||||
uchar *d = mtdBuffer->Get(c);
|
||||
if (d) {
|
||||
if (c >= TS_SIZE) {
|
||||
TsSetPid(d, mtdMapper->UniqToRealPid(TsPid(d)));
|
||||
delivered = true;
|
||||
}
|
||||
else
|
||||
d = NULL;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
int cMtdCamSlot::PutData(const uchar *Data, int Count)
|
||||
{
|
||||
return mtdBuffer->Put(Data, Count);
|
||||
}
|
||||
|
||||
int cMtdCamSlot::PutCat(const uchar *Data, int Count)
|
||||
{
|
||||
MasterSlot()->Decrypt(const_cast<uchar *>(Data), Count);
|
||||
return Count;
|
||||
}
|
187
mtd.h
Normal file
187
mtd.h
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* mtd.h: Multi Transponder Decryption
|
||||
*
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: mtd.h 1.1 2017/03/17 16:06:34 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __MTD_H
|
||||
#define __MTD_H
|
||||
|
||||
/*
|
||||
Multiple Transponder Decryption (MTD) is the method of sending TS packets
|
||||
from channels on different transponders to one single CAM for decryption.
|
||||
While decrypting several channels from the same transponder ("Multi Channel
|
||||
Decryption") is straightforward, because the PIDs are unique within one
|
||||
transponder, channels on different transponders might use the same PIDs
|
||||
for different streams.
|
||||
|
||||
Here's a summary of how MTD is implemented in VDR:
|
||||
|
||||
Identifying the relevant source code
|
||||
------------------------------------
|
||||
|
||||
The actual code that implements the MTD handling is located in the files
|
||||
mtd.h and mtd.c. There are also a few places in ci.[hc], device.c and
|
||||
menu.c where things need to be handled differently for MTD. All functions
|
||||
and variables that have to do with MTD have the three letters "mtd" (upper-
|
||||
and/or lowercase) in their name, so that these code lines can be easily
|
||||
identified if necessary.
|
||||
|
||||
What a plugin implementing a cCiAdapter/cCamSlot needs to do
|
||||
------------------------------------------------------------
|
||||
|
||||
If an implementation of cCiAdapter/cCamSlot supports MTD, it needs to
|
||||
fulfill the following requirements:
|
||||
- The cCiAdapter's Assign() function needs to return true for any given
|
||||
device.
|
||||
- The cCamSlot's constructor needs to call MtdEnable().
|
||||
- The cCamSlot's Decrypt() function shall accept the given TS packet,
|
||||
but shall *not* return a decrypted packet. Decypted packets shall be
|
||||
delivered through a call to MtdPutData(), one at a time.
|
||||
- The cCamSlot's Decrypt() function needs to be thread safe, because
|
||||
it will be called from several cMtdCamSlot objects.
|
||||
|
||||
Physical vs. virtual CAMs
|
||||
-------------------------
|
||||
|
||||
MTD is done by having one physical CAM (accessed through a plugin's
|
||||
implementation of cCiAdapter/cCamSlot) and several "virtual" CAMs,
|
||||
implemented through cMtdCamSlot objects ("MTD CAMs"). For each device
|
||||
that requires the physical CAM, one instance of a cMtdCamSlot is created
|
||||
on the fly at runtime, and that MTD CAM is assigned to the device.
|
||||
The MTD CAM takes care of mapping the PIDs, and a cMtdHandler in the
|
||||
physical CAM object distributes the decrypted TS packets to the proper
|
||||
devices.
|
||||
|
||||
Mapping the PIDs
|
||||
----------------
|
||||
|
||||
The main problem with MTD is that the TS packets from different devices
|
||||
(and thus different transponders with possibly overlapping PIDs) need to
|
||||
be combined into one stream, sent to the physical CAM, and finally be sorted
|
||||
apart again and returned to the devices they came from. Both aspects are
|
||||
solved in VDR by mapping the "real" PIDs into "unique" PIDs. Real PIDs
|
||||
are in the range 0x0000-0x1FFF (13 bit). Unique PIDs use the upper 5 bit
|
||||
to indicate the number of the MTD CAM a TS packet came from, and the lower
|
||||
8 bit as individual PID values. Mapping is done with a single array lookup
|
||||
and is thus very fast. The cMtdHandler class takes care of distributing
|
||||
the TS packets to the individual cMtdCamSlot objects, while mapping the
|
||||
PIDs (in both directions) is done by the cMtdMapper class.
|
||||
|
||||
Mapping the SIDs
|
||||
----------------
|
||||
|
||||
Besides the PIDs there are also the "service ids" (SIDs, a.k.a. "programme
|
||||
numbers" or PNRs) that need to be taken care of. SIDs only appear in the
|
||||
CA-PMTs sent to the CAM, so they only need to be mapped from real to unique
|
||||
(not the other way) and since the are only mapped when switching channels,
|
||||
mapping doesn't need to be very fast. Mapping SIDs is also done by the
|
||||
cMtdMapper class.
|
||||
|
||||
Handling the CAT
|
||||
----------------
|
||||
|
||||
Each transponder carries a CAT ("Conditional Access Table") with the fixed PID 1.
|
||||
The CAT contains a list of EMM PIDs, which are necessary to convey entitlement
|
||||
messages to the smart card. Since the CAM only recognizes the CAT if it has
|
||||
its fixed PID of 1, this PID cannot be mapped and has to be sent to the CAM
|
||||
as is. However, the cCaPidReceiver also needs to see the CAM in order to
|
||||
request from the device the TS packets with the EMM PIDs. Since any receivers
|
||||
only get the TS packets after they have been sent through the CAM, we need
|
||||
to send the CAT in both ways, with mapped PID but unmapped EMM PIDs for the
|
||||
cCaPidReceiver, and with unmapped PID but mapped EMM PIDs for the CAM itself.
|
||||
Since the PID 0x0001 can always be distinguished from any mapped PID (which
|
||||
always have a non-zero upper byte), the CAT can be easily channeled in both
|
||||
ways.
|
||||
|
||||
Handling the CA-PMTs
|
||||
--------------------
|
||||
|
||||
The CA-PMTs that are sent to the CAM contain both SIDs and PIDs, which are
|
||||
mapped in cCiCaPmt::MtdMapPids().
|
||||
*/
|
||||
|
||||
#include "ci.h"
|
||||
#include "remux.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
class cMtdHandler {
|
||||
private:
|
||||
cVector<cMtdCamSlot *> camSlots;
|
||||
public:
|
||||
cMtdHandler(void);
|
||||
///< Creates a new MTD handler that distributes TS data received through
|
||||
///< calls to the Put() function to the individual CAM slots that have been
|
||||
///< created via GetMtdCamSlot(). It also distributes several function
|
||||
///< calls from the physical master CAM slot to the individual MTD CAM slots.
|
||||
~cMtdHandler();
|
||||
cMtdCamSlot *GetMtdCamSlot(cCamSlot *MasterSlot);
|
||||
///< Creates a new MTD CAM slot, or reuses an existing one that is currently
|
||||
///< unused.
|
||||
int Put(const uchar *Data, int Count);
|
||||
///< Puts at most Count bytes of Data into the CAM slot which's index is
|
||||
///< derived from the PID of the TS packets.
|
||||
///< Returns the number of bytes actually stored.
|
||||
int Priority(void);
|
||||
///< Returns the maximum priority of any of the active MTD CAM slots.
|
||||
bool IsDecrypting(void);
|
||||
///< Returns true if any of the active MTD CAM slots is currently decrypting.
|
||||
void StartDecrypting(void);
|
||||
///< Tells all active MTD CAM slots to start decrypting.
|
||||
void CancelActivation(void);
|
||||
///< Tells all active MTD CAM slots to cancel activation.
|
||||
bool IsActivating(void);
|
||||
///< Returns true if any of the active MTD CAM slots is currently activating.
|
||||
bool Devices(cVector<int> &CardIndexes);
|
||||
///< Adds the card indexes of the devices of any active MTD CAM slots to
|
||||
///< the given CardIndexes.
|
||||
///< Returns true if the array is not empty.
|
||||
};
|
||||
|
||||
#define MTD_DONT_CALL(v) dsyslog("PROGRAMMING ERROR (%s,%d): DON'T CALL %s", __FILE__, __LINE__, __FUNCTION__); return v;
|
||||
|
||||
class cMtdMapper;
|
||||
|
||||
void MtdMapSid(uchar *p, cMtdMapper *MtdMapper);
|
||||
void MtdMapPid(uchar *p, cMtdMapper *MtdMapper);
|
||||
|
||||
class cMtdCamSlot : public cCamSlot {
|
||||
private:
|
||||
cMtdMapper *mtdMapper;
|
||||
cRingBufferLinear *mtdBuffer;
|
||||
bool delivered;
|
||||
protected:
|
||||
virtual const int *GetCaSystemIds(void);
|
||||
virtual void SendCaPmt(uint8_t CmdId);
|
||||
public:
|
||||
cMtdCamSlot(cCamSlot *MasterSlot, int Index);
|
||||
///< Creates a new "Multi Transponder Decryption" CAM slot, connected to the
|
||||
///< given physical MasterSlot, using the given Index for mapping PIDs.
|
||||
virtual ~cMtdCamSlot();
|
||||
cMtdMapper *MtdMapper(void) { return mtdMapper; }
|
||||
virtual bool RepliesToQuery(void);
|
||||
virtual bool ProvidesCa(const int *CaSystemIds);
|
||||
virtual bool CanDecrypt(const cChannel *Channel);
|
||||
virtual void StartDecrypting(void);
|
||||
virtual void StopDecrypting(void);
|
||||
virtual bool IsDecrypting(void);
|
||||
virtual uchar *Decrypt(uchar *Data, int &Count);
|
||||
int PutData(const uchar *Data, int Count);
|
||||
int PutCat(const uchar *Data, int Count);
|
||||
// The following functions shall not be called for a cMtdCamSlot:
|
||||
virtual cCamSlot *Spawn(void) { MTD_DONT_CALL(NULL); }
|
||||
virtual bool Reset(void) { MTD_DONT_CALL(false); }
|
||||
virtual eModuleStatus ModuleStatus(void) { MTD_DONT_CALL(msNone); }
|
||||
virtual const char *GetCamName(void) { MTD_DONT_CALL(NULL); }
|
||||
virtual bool Ready(void) { MTD_DONT_CALL(false); }
|
||||
virtual bool HasMMI(void) { MTD_DONT_CALL(false); }
|
||||
virtual bool HasUserIO(void) { MTD_DONT_CALL(false); }
|
||||
virtual bool EnterMenu(void) { MTD_DONT_CALL(false); }
|
||||
virtual cCiMenu *GetMenu(void) { MTD_DONT_CALL(NULL); }
|
||||
virtual cCiEnquiry *GetEnquiry(void) { MTD_DONT_CALL(NULL); }
|
||||
};
|
||||
|
||||
#endif //__MTD_H
|
8
remux.h
8
remux.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: remux.h 4.1 2016/12/22 13:09:54 kls Exp $
|
||||
* $Id: remux.h 4.2 2017/02/27 16:11:57 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __REMUX_H
|
||||
@ -83,6 +83,12 @@ inline int TsPid(const uchar *p)
|
||||
return (p[1] & TS_PID_MASK_HI) * 256 + p[2];
|
||||
}
|
||||
|
||||
inline void TsSetPid(uchar *p, int Pid)
|
||||
{
|
||||
p[1] = (p[1] & ~TS_PID_MASK_HI) | ((Pid >> 8) & TS_PID_MASK_HI);
|
||||
p[2] = Pid & 0x00FF;
|
||||
}
|
||||
|
||||
inline bool TsIsScrambled(const uchar *p)
|
||||
{
|
||||
return p[3] & TS_SCRAMBLING_CONTROL;
|
||||
|
18
tools.h
18
tools.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: tools.h 4.5 2016/12/23 13:56:35 kls Exp $
|
||||
* $Id: tools.h 4.6 2017/03/16 16:04:43 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __TOOLS_H
|
||||
@ -242,6 +242,17 @@ cString dtoa(double d, const char *Format = "%f");
|
||||
///< the decimal point, independent of the currently selected locale.
|
||||
///< If Format is given, it will be used instead of the default.
|
||||
cString itoa(int n);
|
||||
inline uint16_t Peek13(const uchar *p)
|
||||
{
|
||||
uint16_t v = uint16_t(*p++ & 0x1F) << 8;
|
||||
return v + (*p & 0xFF);
|
||||
}
|
||||
inline void Poke13(uchar *p, uint16_t v)
|
||||
{
|
||||
v |= uint16_t(*p & ~0x1F) << 8;
|
||||
*p++ = v >> 8;
|
||||
*p = v & 0xFF;
|
||||
}
|
||||
cString AddDirectory(const char *DirName, const char *FileName);
|
||||
bool EntriesOnSameFileSystem(const char *File1, const char *File2);
|
||||
///< Checks whether the given files are on the same file system. If either of the
|
||||
@ -744,6 +755,11 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
inline int CompareInts(const void *a, const void *b)
|
||||
{
|
||||
return *(const int *)a > *(const int *)b;
|
||||
}
|
||||
|
||||
inline int CompareStrings(const void *a, const void *b)
|
||||
{
|
||||
return strcmp(*(const char **)a, *(const char **)b);
|
||||
|
Loading…
Reference in New Issue
Block a user