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
|
- Added a short sleep to cTSBuffer::Action() to avoid high CPU usage (thanks to
|
||||||
Sergey Chernyavskiy).
|
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).
|
- Added 'S3W ABS-3A' to sources.conf (thanks to Frank Richter).
|
||||||
- Fixed a possible deadlock in the recordings handler thread.
|
- Fixed a possible deadlock in the recordings handler thread.
|
||||||
@ -8914,3 +8914,7 @@ Video Disk Recorder Revision History
|
|||||||
contained an empty string).
|
contained an empty string).
|
||||||
- PIDs can now be added to and deleted from a cReceiver while it is attached to
|
- 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.
|
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
|
# See the main source file 'vdr.c' for copyright information and
|
||||||
# how to reach the author.
|
# 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:
|
.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\
|
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\
|
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\
|
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\
|
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
|
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
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* 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"
|
#include "ci.h"
|
||||||
@ -19,6 +19,7 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
|
#include "mtd.h"
|
||||||
#include "pat.h"
|
#include "pat.h"
|
||||||
#include "receiver.h"
|
#include "receiver.h"
|
||||||
#include "remux.h"
|
#include "remux.h"
|
||||||
@ -118,6 +119,7 @@ private:
|
|||||||
cVector<int> emmPids;
|
cVector<int> emmPids;
|
||||||
uchar buffer[2048]; // 11 bit length, max. 2048 byte
|
uchar buffer[2048]; // 11 bit length, max. 2048 byte
|
||||||
uchar *bufp;
|
uchar *bufp;
|
||||||
|
uchar mtdCatBuffer[TS_SIZE]; // TODO: handle multi packet CATs!
|
||||||
int length;
|
int length;
|
||||||
void AddEmmPid(int Pid);
|
void AddEmmPid(int Pid);
|
||||||
void DelEmmPids(void);
|
void DelEmmPids(void);
|
||||||
@ -157,6 +159,7 @@ void cCaPidReceiver::DelEmmPids(void)
|
|||||||
void cCaPidReceiver::Receive(const uchar *Data, int Length)
|
void cCaPidReceiver::Receive(const uchar *Data, int Length)
|
||||||
{
|
{
|
||||||
if (TsPid(Data) == CATPID) {
|
if (TsPid(Data) == CATPID) {
|
||||||
|
cMtdCamSlot *MtdCamSlot = dynamic_cast<cMtdCamSlot *>(Device()->CamSlot());
|
||||||
const uchar *p = NULL;
|
const uchar *p = NULL;
|
||||||
if (TsPayloadStart(Data)) {
|
if (TsPayloadStart(Data)) {
|
||||||
if (Data[5] == SI::TableIdCAT) {
|
if (Data[5] == SI::TableIdCAT) {
|
||||||
@ -166,6 +169,8 @@ void cCaPidReceiver::Receive(const uchar *Data, int Length)
|
|||||||
if (v != catVersion) {
|
if (v != catVersion) {
|
||||||
if (Data[11] == 0 && Data[12] == 0) { // section number, last section number
|
if (Data[11] == 0 && Data[12] == 0) { // section number, last section number
|
||||||
if (length > TS_SIZE - 8) {
|
if (length > TS_SIZE - 8) {
|
||||||
|
if (MtdCamSlot)
|
||||||
|
esyslog("ERROR: need to implement multi packet CAT handling for MTD!");
|
||||||
int n = TS_SIZE - 13;
|
int n = TS_SIZE - 13;
|
||||||
memcpy(buffer, Data + 13, n);
|
memcpy(buffer, Data + 13, n);
|
||||||
bufp = buffer + n;
|
bufp = buffer + n;
|
||||||
@ -180,6 +185,8 @@ void cCaPidReceiver::Receive(const uchar *Data, int Length)
|
|||||||
dsyslog("multi table CAT section - unhandled!");
|
dsyslog("multi table CAT section - unhandled!");
|
||||||
catVersion = v;
|
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
|
for (int i = 0; i < length - 4; i++) { // -4 = checksum
|
||||||
if (p[i] == 0x09) {
|
if (p[i] == 0x09) {
|
||||||
int CaId = int(p[i + 2] << 8) | p[i + 3];
|
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);
|
AddEmmPid(EmmPid);
|
||||||
|
if (MtdCamSlot)
|
||||||
|
MtdMapPid(const_cast<uchar *>(p + i + 4), MtdCamSlot->MtdMapper());
|
||||||
switch (CaId >> 8) {
|
switch (CaId >> 8) {
|
||||||
case 0x01: for (int j = i + 7; j < p[i + 1] + 2; j += 4) {
|
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);
|
AddEmmPid(EmmPid);
|
||||||
|
if (MtdCamSlot)
|
||||||
|
MtdMapPid(const_cast<uchar *>(p + j), MtdCamSlot->MtdMapper());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -220,6 +231,9 @@ void cCaPidReceiver::Receive(const uchar *Data, int Length)
|
|||||||
p = NULL;
|
p = NULL;
|
||||||
bufp = 0;
|
bufp = 0;
|
||||||
length = 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) {
|
else if (Now - lastScrambledTime > UNSCRAMBLE_TIME) {
|
||||||
dsyslog("CAM %d: activated!", camSlot->SlotNumber());
|
dsyslog("CAM %d: activated!", camSlot->SlotNumber());
|
||||||
Skins.QueueMessage(mtInfo, tr("CAM activated!"));
|
Skins.QueueMessage(mtInfo, tr("CAM activated!"));
|
||||||
|
cDevice *d = Device();
|
||||||
Detach();
|
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);
|
void SetListManagement(uint8_t ListManagement);
|
||||||
uint8_t ListManagement(void) { return capmt.Get(0); }
|
uint8_t ListManagement(void) { return capmt.Get(0); }
|
||||||
void AddPid(int Pid, uint8_t StreamType);
|
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)
|
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!");
|
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 -------------------------------------------
|
// --- cCiConditionalAccessSupport -------------------------------------------
|
||||||
|
|
||||||
// CA Enable Ids:
|
// CA Enable Ids:
|
||||||
@ -897,8 +995,12 @@ void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
|
|||||||
case AOT_CA_PMT_REPLY: {
|
case AOT_CA_PMT_REPLY: {
|
||||||
dbgprotocol("Slot %d: <== Ca Pmt Reply (%d)", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: <== Ca Pmt Reply (%d)", Tc()->CamSlot()->SlotNumber(), SessionId());
|
||||||
if (!repliesToQuery) {
|
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;
|
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
|
state = 5; // got ca pmt reply
|
||||||
int l = 0;
|
int l = 0;
|
||||||
@ -1650,6 +1752,14 @@ public:
|
|||||||
programNumber = ProgramNumber;
|
programNumber = ProgramNumber;
|
||||||
modified = false;
|
modified = false;
|
||||||
}
|
}
|
||||||
|
bool Active(void)
|
||||||
|
{
|
||||||
|
for (cCiCaPidData *p = pidList.First(); p; p = pidList.Next(p)) {
|
||||||
|
if (p->active)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- cCiAdapter ------------------------------------------------------------
|
// --- cCiAdapter ------------------------------------------------------------
|
||||||
@ -1725,15 +1835,18 @@ cCamSlot::cCamSlot(cCiAdapter *CiAdapter, bool WantsTsData, cCamSlot *MasterSlot
|
|||||||
caPidReceiver = WantsTsData ? new cCaPidReceiver : NULL;
|
caPidReceiver = WantsTsData ? new cCaPidReceiver : NULL;
|
||||||
caActivationReceiver = NULL;
|
caActivationReceiver = NULL;
|
||||||
slotIndex = -1;
|
slotIndex = -1;
|
||||||
|
mtdAvailable = false;
|
||||||
|
mtdHandler = NULL;
|
||||||
lastModuleStatus = msReset; // avoids initial reset log message
|
lastModuleStatus = msReset; // avoids initial reset log message
|
||||||
resetTime = 0;
|
resetTime = 0;
|
||||||
resendPmt = false;
|
resendPmt = false;
|
||||||
source = transponder = 0;
|
|
||||||
for (int i = 0; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) // tc[0] is not used, but initialized anyway
|
for (int i = 0; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) // tc[0] is not used, but initialized anyway
|
||||||
tc[i] = NULL;
|
tc[i] = NULL;
|
||||||
|
if (MasterSlot)
|
||||||
|
slotNumber = MasterSlot->SlotNumber();
|
||||||
|
if (ciAdapter) {
|
||||||
CamSlots.Add(this);
|
CamSlots.Add(this);
|
||||||
slotNumber = Index() + 1;
|
slotNumber = Index() + 1;
|
||||||
if (ciAdapter) {
|
|
||||||
ciAdapter->AddCamSlot(this);
|
ciAdapter->AddCamSlot(this);
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
@ -1747,26 +1860,38 @@ cCamSlot::~cCamSlot()
|
|||||||
delete caActivationReceiver;
|
delete caActivationReceiver;
|
||||||
CamSlots.Del(this, false);
|
CamSlots.Del(this, false);
|
||||||
DeleteAllConnections();
|
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)
|
bool cCamSlot::Assign(cDevice *Device, bool Query)
|
||||||
{
|
{
|
||||||
cMutexLock MutexLock(&mutex);
|
cMutexLock MutexLock(&mutex);
|
||||||
|
if (Device == assignedDevice)
|
||||||
|
return true;
|
||||||
if (ciAdapter) {
|
if (ciAdapter) {
|
||||||
if (ciAdapter->Assign(Device, true)) {
|
int OldDeviceNumber = 0;
|
||||||
if (!Device && assignedDevice) {
|
if (assignedDevice && !Query) {
|
||||||
|
OldDeviceNumber = assignedDevice->DeviceNumber() + 1;
|
||||||
if (caPidReceiver)
|
if (caPidReceiver)
|
||||||
assignedDevice->Detach(caPidReceiver);
|
assignedDevice->Detach(caPidReceiver);
|
||||||
assignedDevice->SetCamSlot(NULL);
|
assignedDevice->SetCamSlot(NULL);
|
||||||
|
assignedDevice = NULL;
|
||||||
}
|
}
|
||||||
if (!Query || !Device) {
|
if (ciAdapter->Assign(Device, true)) {
|
||||||
|
if (!Query) {
|
||||||
StopDecrypting();
|
StopDecrypting();
|
||||||
source = transponder = 0;
|
|
||||||
if (ciAdapter->Assign(Device)) {
|
if (ciAdapter->Assign(Device)) {
|
||||||
int OldDeviceNumber = assignedDevice ? assignedDevice->DeviceNumber() + 1 : 0;
|
|
||||||
assignedDevice = Device;
|
|
||||||
if (Device) {
|
if (Device) {
|
||||||
Device->SetCamSlot(this);
|
Device->SetCamSlot(this);
|
||||||
|
assignedDevice = Device;
|
||||||
if (caPidReceiver) {
|
if (caPidReceiver) {
|
||||||
caPidReceiver->Reset();
|
caPidReceiver->Reset();
|
||||||
Device->AttachReceiver(caPidReceiver);
|
Device->AttachReceiver(caPidReceiver);
|
||||||
@ -1787,6 +1912,16 @@ bool cCamSlot::Assign(cDevice *Device, bool Query)
|
|||||||
return false;
|
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)
|
void cCamSlot::NewConnection(void)
|
||||||
{
|
{
|
||||||
cMutexLock MutexLock(&mutex);
|
cMutexLock MutexLock(&mutex);
|
||||||
@ -1812,8 +1947,6 @@ void cCamSlot::DeleteAllConnections(void)
|
|||||||
void cCamSlot::Process(cTPDU *TPDU)
|
void cCamSlot::Process(cTPDU *TPDU)
|
||||||
{
|
{
|
||||||
cMutexLock MutexLock(&mutex);
|
cMutexLock MutexLock(&mutex);
|
||||||
if (caActivationReceiver && !caActivationReceiver->IsAttached())
|
|
||||||
CancelActivation();
|
|
||||||
if (TPDU) {
|
if (TPDU) {
|
||||||
int n = TPDU->Tcid();
|
int n = TPDU->Tcid();
|
||||||
if (1 <= n && n <= MAX_CONNECTIONS_PER_CAM_SLOT) {
|
if (1 <= n && n <= MAX_CONNECTIONS_PER_CAM_SLOT) {
|
||||||
@ -1836,6 +1969,7 @@ void cCamSlot::Process(cTPDU *TPDU)
|
|||||||
case msNone:
|
case msNone:
|
||||||
dbgprotocol("Slot %d: no module present\n", slotNumber);
|
dbgprotocol("Slot %d: no module present\n", slotNumber);
|
||||||
isyslog("CAM %d: no module present", slotNumber);
|
isyslog("CAM %d: no module present", slotNumber);
|
||||||
|
MtdActivate(false);
|
||||||
DeleteAllConnections();
|
DeleteAllConnections();
|
||||||
CancelActivation();
|
CancelActivation();
|
||||||
break;
|
break;
|
||||||
@ -1852,7 +1986,7 @@ void cCamSlot::Process(cTPDU *TPDU)
|
|||||||
dbgprotocol("Slot %d: module ready\n", slotNumber);
|
dbgprotocol("Slot %d: module ready\n", slotNumber);
|
||||||
isyslog("CAM %d: module ready", slotNumber);
|
isyslog("CAM %d: module ready", slotNumber);
|
||||||
NewConnection();
|
NewConnection();
|
||||||
resendPmt = caProgramList.Count() > 0;
|
resendPmt = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
esyslog("ERROR: unknown module status %d (%s)", ms, __FUNCTION__);
|
esyslog("ERROR: unknown module status %d (%s)", ms, __FUNCTION__);
|
||||||
@ -1861,8 +1995,14 @@ void cCamSlot::Process(cTPDU *TPDU)
|
|||||||
}
|
}
|
||||||
moduleCheckTimer.Set(MODULE_CHECK_INTERVAL);
|
moduleCheckTimer.Set(MODULE_CHECK_INTERVAL);
|
||||||
}
|
}
|
||||||
if (resendPmt)
|
if (resendPmt && Ready()) {
|
||||||
SendCaPmt(CPCI_OK_DESCRAMBLING);
|
if (mtdHandler) {
|
||||||
|
mtdHandler->StartDecrypting();
|
||||||
|
resendPmt = false;
|
||||||
|
}
|
||||||
|
else if (caProgramList.Count())
|
||||||
|
StartDecrypting();
|
||||||
|
}
|
||||||
processed.Broadcast();
|
processed.Broadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1922,12 +2062,18 @@ void cCamSlot::StartActivation(void)
|
|||||||
void cCamSlot::CancelActivation(void)
|
void cCamSlot::CancelActivation(void)
|
||||||
{
|
{
|
||||||
cMutexLock MutexLock(&mutex);
|
cMutexLock MutexLock(&mutex);
|
||||||
|
if (mtdHandler)
|
||||||
|
mtdHandler->CancelActivation();
|
||||||
|
else {
|
||||||
delete caActivationReceiver;
|
delete caActivationReceiver;
|
||||||
caActivationReceiver = NULL;
|
caActivationReceiver = NULL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool cCamSlot::IsActivating(void)
|
bool cCamSlot::IsActivating(void)
|
||||||
{
|
{
|
||||||
|
if (mtdHandler)
|
||||||
|
return mtdHandler->IsActivating();
|
||||||
return caActivationReceiver;
|
return caActivationReceiver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2001,52 +2147,111 @@ cCiEnquiry *cCamSlot::GetEnquiry(void)
|
|||||||
return NULL;
|
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);
|
cMutexLock MutexLock(&mutex);
|
||||||
cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
|
cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
|
||||||
if (cas) {
|
return cas && cas->RepliesToQuery();
|
||||||
const int *CaSystemIds = cas->GetCaSystemIds();
|
}
|
||||||
|
|
||||||
|
void cCamSlot::BuildCaPmts(uint8_t CmdId, cCiCaPmtList &CaPmtList, cMtdMapper *MtdMapper)
|
||||||
|
{
|
||||||
|
cMutexLock MutexLock(&mutex);
|
||||||
|
CaPmtList.caPmts.Clear();
|
||||||
|
const int *CaSystemIds = GetCaSystemIds();
|
||||||
if (CaSystemIds && *CaSystemIds) {
|
if (CaSystemIds && *CaSystemIds) {
|
||||||
if (caProgramList.Count()) {
|
if (caProgramList.Count()) {
|
||||||
for (int Loop = 1; Loop <= 2; Loop++) {
|
|
||||||
for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
|
for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
|
||||||
if (p->modified || resendPmt) {
|
if (p->modified || resendPmt) {
|
||||||
bool Active = false;
|
bool Active = p->Active();
|
||||||
cCiCaPmt CaPmt(CmdId, source, transponder, p->programNumber, CaSystemIds);
|
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)) {
|
for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
|
||||||
if (q->active) {
|
if (q->active)
|
||||||
CaPmt.AddPid(q->pid, q->streamType);
|
CaPmt->AddPid(q->pid, q->streamType);
|
||||||
Active = true;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if ((Loop == 1) != Active) { // first remove, then add
|
|
||||||
if (caPidReceiver) {
|
if (caPidReceiver) {
|
||||||
int CaPids[MAXRECEIVEPIDS + 1];
|
int CaPids[MAXRECEIVEPIDS + 1];
|
||||||
if (GetCaPids(source, transponder, p->programNumber, CaSystemIds, MAXRECEIVEPIDS + 1, CaPids) > 0) {
|
if (GetCaPids(source, transponder, p->programNumber, CaSystemIds, MAXRECEIVEPIDS + 1, CaPids) > 0) {
|
||||||
if (Loop == 1)
|
if (Active)
|
||||||
caPidReceiver->DelPids(CaPids);
|
|
||||||
else
|
|
||||||
caPidReceiver->AddPids(CaPids);
|
caPidReceiver->AddPids(CaPids);
|
||||||
|
else
|
||||||
|
caPidReceiver->DelPids(CaPids);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cas->RepliesToQuery())
|
if (RepliesToQuery())
|
||||||
CaPmt.SetListManagement(Active ? CPLM_ADD : CPLM_UPDATE);
|
CaPmt->SetListManagement(Active ? CPLM_ADD : CPLM_UPDATE);
|
||||||
if (Active || cas->RepliesToQuery())
|
if (MtdMapper)
|
||||||
cas->SendPMT(&CaPmt);
|
CaPmt->MtdMapPids(MtdMapper);
|
||||||
p->modified = false;
|
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;
|
resendPmt = false;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
cCiCaPmt CaPmt(CmdId, 0, 0, 0, NULL);
|
void cCamSlot::SendCaPmt(uint8_t CmdId)
|
||||||
cas->SendPMT(&CaPmt);
|
{
|
||||||
|
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)
|
const int *cCamSlot::GetCaSystemIds(void)
|
||||||
@ -2058,6 +2263,8 @@ const int *cCamSlot::GetCaSystemIds(void)
|
|||||||
|
|
||||||
int cCamSlot::Priority(void)
|
int cCamSlot::Priority(void)
|
||||||
{
|
{
|
||||||
|
if (mtdHandler)
|
||||||
|
return mtdHandler->Priority();
|
||||||
cDevice *d = Device();
|
cDevice *d = Device();
|
||||||
return d ? d->Priority() : IDLEPRIORITY;
|
return d ? d->Priority() : IDLEPRIORITY;
|
||||||
}
|
}
|
||||||
@ -2178,15 +2385,14 @@ void cCamSlot::StartDecrypting(void)
|
|||||||
void cCamSlot::StopDecrypting(void)
|
void cCamSlot::StopDecrypting(void)
|
||||||
{
|
{
|
||||||
cMutexLock MutexLock(&mutex);
|
cMutexLock MutexLock(&mutex);
|
||||||
if (caProgramList.Count()) {
|
|
||||||
caProgramList.Clear();
|
caProgramList.Clear();
|
||||||
SendCaPmt(CPCI_NOT_SELECTED);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cCamSlot::IsDecrypting(void)
|
bool cCamSlot::IsDecrypting(void)
|
||||||
{
|
{
|
||||||
cMutexLock MutexLock(&mutex);
|
cMutexLock MutexLock(&mutex);
|
||||||
|
if (mtdHandler)
|
||||||
|
return mtdHandler->IsDecrypting();
|
||||||
if (caProgramList.Count()) {
|
if (caProgramList.Count()) {
|
||||||
for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
|
for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
|
||||||
if (p->modified)
|
if (p->modified)
|
||||||
|
61
ci.h
61
ci.h
@ -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 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
|
#ifndef __CI_H
|
||||||
@ -13,6 +13,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "channels.h"
|
#include "channels.h"
|
||||||
|
#include "ringbuffer.h"
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
|
||||||
@ -124,10 +125,23 @@ class cCiSession;
|
|||||||
class cCiCaProgramData;
|
class cCiCaProgramData;
|
||||||
class cCaPidReceiver;
|
class cCaPidReceiver;
|
||||||
class cCaActivationReceiver;
|
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 {
|
class cCamSlot : public cListObject {
|
||||||
friend class cCiAdapter;
|
friend class cCiAdapter;
|
||||||
friend class cCiTransportConnection;
|
friend class cCiTransportConnection;
|
||||||
|
friend class cCiConditionalAccessSupport;
|
||||||
|
friend class cMtdCamSlot;
|
||||||
private:
|
private:
|
||||||
cMutex mutex;
|
cMutex mutex;
|
||||||
cCondVar processed;
|
cCondVar processed;
|
||||||
@ -146,13 +160,40 @@ private:
|
|||||||
int source;
|
int source;
|
||||||
int transponder;
|
int transponder;
|
||||||
cList<cCiCaProgramData> caProgramList;
|
cList<cCiCaProgramData> caProgramList;
|
||||||
const int *GetCaSystemIds(void);
|
bool mtdAvailable;
|
||||||
void SendCaPmt(uint8_t CmdId);
|
cMtdHandler *mtdHandler;
|
||||||
void NewConnection(void);
|
void NewConnection(void);
|
||||||
void DeleteAllConnections(void);
|
void DeleteAllConnections(void);
|
||||||
void Process(cTPDU *TPDU = NULL);
|
void Process(cTPDU *TPDU = NULL);
|
||||||
void Write(cTPDU *TPDU);
|
void Write(cTPDU *TPDU);
|
||||||
cCiSession *GetSessionByResourceId(uint32_t ResourceId);
|
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:
|
public:
|
||||||
cCamSlot(cCiAdapter *CiAdapter, bool WantsTsData = false, cCamSlot *MasterSlot = NULL);
|
cCamSlot(cCiAdapter *CiAdapter, bool WantsTsData = false, cCamSlot *MasterSlot = NULL);
|
||||||
///< Creates a new CAM slot for the given CiAdapter.
|
///< Creates a new CAM slot for the given CiAdapter.
|
||||||
@ -175,6 +216,13 @@ public:
|
|||||||
cCamSlot *MasterSlot(void) { return masterSlot ? masterSlot : this; }
|
cCamSlot *MasterSlot(void) { return masterSlot ? masterSlot : this; }
|
||||||
///< Returns this CAM slot's master slot, or a pointer to itself if it is a
|
///< Returns this CAM slot's master slot, or a pointer to itself if it is a
|
||||||
///< master slot.
|
///< 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);
|
virtual bool Assign(cDevice *Device, bool Query = false);
|
||||||
///< Assigns this CAM slot to the given Device, if this is possible.
|
///< 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
|
///< If Query is 'true', the CI adapter of this slot only checks whether
|
||||||
@ -190,6 +238,10 @@ public:
|
|||||||
///< class function.
|
///< class function.
|
||||||
cDevice *Device(void) { return assignedDevice; }
|
cDevice *Device(void) { return assignedDevice; }
|
||||||
///< Returns the device this CAM slot is currently assigned to.
|
///< 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; }
|
bool WantsTsData(void) const { return caPidReceiver != NULL; }
|
||||||
///< Returns true if this CAM slot wants to receive the TS data through
|
///< Returns true if this CAM slot wants to receive the TS data through
|
||||||
///< its Decrypt() function.
|
///< its Decrypt() function.
|
||||||
@ -308,7 +360,8 @@ public:
|
|||||||
///< the CAM's control). If no decrypted TS packet is currently available, NULL
|
///< 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 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
|
///< 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
|
///< A derived class that implements this function will also need
|
||||||
///< to set the WantsTsData parameter in the call to the base class
|
///< to set the WantsTsData parameter in the call to the base class
|
||||||
///< constructor to true in order to receive the TS data.
|
///< 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
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* 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"
|
#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
|
continue; // CAM slot can't be used with this device
|
||||||
bool ndr;
|
bool ndr;
|
||||||
if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basically able to do the job
|
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
|
ndr = true; // using a different CAM slot requires detaching receivers
|
||||||
|
}
|
||||||
|
}
|
||||||
// Put together an integer number that reflects the "impact" using
|
// Put together an integer number that reflects the "impact" using
|
||||||
// this device would have on the overall system. Each condition is represented
|
// 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
|
// 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)
|
if (NeedsDetachReceivers)
|
||||||
d->DetachAllReceivers();
|
d->DetachAllReceivers();
|
||||||
if (s) {
|
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() != d) {
|
||||||
if (s->Device())
|
// CAM slot s is currently assigned to a different device than d
|
||||||
|
if (Priority > s->Priority()) {
|
||||||
s->Device()->DetachAllReceivers();
|
s->Device()->DetachAllReceivers();
|
||||||
if (d->CamSlot())
|
|
||||||
d->CamSlot()->Assign(NULL);
|
d->CamSlot()->Assign(NULL);
|
||||||
s->Assign(d);
|
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())
|
else if (d->CamSlot() && !d->CamSlot()->IsDecrypting())
|
||||||
d->CamSlot()->Assign(NULL);
|
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
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* 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"
|
#include "menu.h"
|
||||||
@ -3770,15 +3770,18 @@ bool cMenuSetupCAMItem::Changed(void)
|
|||||||
else if (camSlot->IsActivating())
|
else if (camSlot->IsActivating())
|
||||||
// TRANSLATORS: note the leading blank!
|
// TRANSLATORS: note the leading blank!
|
||||||
Activating = tr(" (activating)");
|
Activating = tr(" (activating)");
|
||||||
|
cVector<int> CardIndexes;
|
||||||
for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
|
for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
|
||||||
if (CamSlot == camSlot || CamSlot->MasterSlot() == camSlot) {
|
if (CamSlot == camSlot || CamSlot->MasterSlot() == camSlot)
|
||||||
if (cDevice *Device = CamSlot->Device()) {
|
CamSlot->Devices(CardIndexes);
|
||||||
if (!**AssignedDevice)
|
}
|
||||||
|
if (CardIndexes.Size() > 0) {
|
||||||
AssignedDevice = cString::sprintf(" %s", tr("@ device"));
|
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);
|
cString buffer = cString::sprintf(" %d %s%s%s", camSlot->SlotNumber(), CamName, *AssignedDevice, Activating);
|
||||||
if (strcmp(buffer, Text()) != 0) {
|
if (strcmp(buffer, Text()) != 0) {
|
||||||
SetText(buffer);
|
SetText(buffer);
|
||||||
@ -3874,9 +3877,9 @@ eOSState cMenuSetupCAM::Activate(void)
|
|||||||
if (cDevice *Device = cDevice::GetDevice(i)) {
|
if (cDevice *Device = cDevice::GetDevice(i)) {
|
||||||
if (Device->ProvidesChannel(Channel)) {
|
if (Device->ProvidesChannel(Channel)) {
|
||||||
if (Device->Priority() < LIVEPRIORITY) { // don't interrupt recordings
|
if (Device->Priority() < LIVEPRIORITY) { // don't interrupt recordings
|
||||||
if (CamSlot->CanActivate()) {
|
|
||||||
if (CamSlot->Assign(Device, true)) { // query
|
if (CamSlot->Assign(Device, true)) { // query
|
||||||
cControl::Shutdown(); // must end transfer mode before assigning CAM, otherwise it might be unassigned again
|
cControl::Shutdown(); // must end transfer mode before assigning CAM, otherwise it might be unassigned again
|
||||||
|
CamSlot = CamSlot->MtdSpawn();
|
||||||
if (CamSlot->Assign(Device)) {
|
if (CamSlot->Assign(Device)) {
|
||||||
if (Device->SwitchChannel(Channel, true)) {
|
if (Device->SwitchChannel(Channel, true)) {
|
||||||
CamSlot->StartActivation();
|
CamSlot->StartActivation();
|
||||||
@ -3890,7 +3893,6 @@ eOSState cMenuSetupCAM::Activate(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Skins.Message(mtError, tr("Can't activate CAM!"));
|
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
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* 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
|
#ifndef __REMUX_H
|
||||||
@ -83,6 +83,12 @@ inline int TsPid(const uchar *p)
|
|||||||
return (p[1] & TS_PID_MASK_HI) * 256 + p[2];
|
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)
|
inline bool TsIsScrambled(const uchar *p)
|
||||||
{
|
{
|
||||||
return p[3] & TS_SCRAMBLING_CONTROL;
|
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
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* 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
|
#ifndef __TOOLS_H
|
||||||
@ -242,6 +242,17 @@ cString dtoa(double d, const char *Format = "%f");
|
|||||||
///< the decimal point, independent of the currently selected locale.
|
///< the decimal point, independent of the currently selected locale.
|
||||||
///< If Format is given, it will be used instead of the default.
|
///< If Format is given, it will be used instead of the default.
|
||||||
cString itoa(int n);
|
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);
|
cString AddDirectory(const char *DirName, const char *FileName);
|
||||||
bool EntriesOnSameFileSystem(const char *File1, const char *File2);
|
bool EntriesOnSameFileSystem(const char *File1, const char *File2);
|
||||||
///< Checks whether the given files are on the same file system. If either of the
|
///< 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)
|
inline int CompareStrings(const void *a, const void *b)
|
||||||
{
|
{
|
||||||
return strcmp(*(const char **)a, *(const char **)b);
|
return strcmp(*(const char **)a, *(const char **)b);
|
||||||
|
Loading…
Reference in New Issue
Block a user