vdr/mtd.c

372 lines
9.5 KiB
C

/*
* 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.16 2020/06/16 14:33:32 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)
{
int Used = 0;
while (Count >= TS_SIZE) {
if (int Skipped = TS_SYNC(Data, Count))
return Used + Skipped;
int Pid = TsPid(Data);
#ifdef KEEPPIDS
int Index = 0;
#else
int Index = (Pid >> UNIQ_PID_SHIFT) - 1;
#endif // KEEPPIDS
if (Index >= 0 && Index < camSlots.Size()) {
int w = camSlots[Index]->PutData(Data, TS_SIZE);
if (w == 0)
break;
else if (w != TS_SIZE)
esyslog("ERROR: incomplete MTD packet written (%d) in PID %d (%04X)", Index + 1, Pid, Pid);
}
else if (Index >= 0) // anything with Index -1 (i.e. MTD number 0) is either garbage or an actual CAT or EIT, which need not be returned to the device
esyslog("ERROR: invalid MTD number (%d) in PID %d (%04X)", Index + 1, Pid, Pid);
Data += TS_SIZE;
Count -= TS_SIZE;
Used += TS_SIZE;
}
return Used;
}
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::StopDecrypting(void)
{
for (int i = 0; i < camSlots.Size(); i++) {
if (camSlots[i]->Device())
camSlots[i]->StopDecrypting();
}
}
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> &DeviceNumbers)
{
for (int i = 0; i < camSlots.Size(); i++)
camSlots[i]->Devices(DeviceNumbers);
return DeviceNumbers.Size() > 0;
}
void cMtdHandler::UnAssignAll(void)
{
for (int i = 0; i < camSlots.Size(); i++)
camSlots[i]->Assign(NULL);
}
// --- cMtdMapper ------------------------------------------------------------
#define MTD_INVALID_PID 0xFFFF
class cMtdMapper {
private:
int number;
int masterCamSlotNumber;
int nextUniqPid;
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;
nextUniqPid = 0;
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 p = 0; p < MAX_UNIQ_PIDS; p++) {
int i = nextUniqPid + p;
if (i >= MAX_UNIQ_PIDS)
i -= MAX_UNIQ_PIDS;
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]);
nextUniqPid = i + 1;
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));
// do not reset nextUniqPid here!
uniqSids.Clear();
}
void MtdMapSid(uchar *p, cMtdMapper *MtdMapper)
{
uint16_t RealSid = p[0] << 8 | p[1];
uint16_t UniqSid = MtdMapper->RealToUniqSid(RealSid);
p[0] = UniqSid >> 8;
p[1] = UniqSid & 0xff;
}
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()
{
Assign(NULL);
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, cMtdMapper *MtdMapper)
{
return MasterSlot()->CanDecrypt(Channel, mtdMapper);
}
void cMtdCamSlot::StartDecrypting(void)
{
MasterSlot()->StartDecrypting();
cCamSlot::StartDecrypting();
}
void cMtdCamSlot::StopDecrypting(void)
{
cCamSlot::StopDecrypting();
cMutexLock MutexLock(&clearMutex);
mtdMapper->Clear();
mtdBuffer->Clear();
delivered = false;
}
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:
cMutexLock MutexLock(&clearMutex);
if (delivered) {
mtdBuffer->Del(TS_SIZE);
delivered = false;
}
// Receive data from buffer:
int c = 0;
uchar *d = mtdBuffer->Get(c);
if (d) {
if (int Skipped = TS_SYNC(d, c)) {
mtdBuffer->Del(Skipped);
return NULL;
}
if (c >= TS_SIZE) {
TsSetPid(d, mtdMapper->UniqToRealPid(TsPid(d)));
delivered = true;
}
else
d = NULL;
}
return d;
}
bool cMtdCamSlot::TsPostProcess(uchar *Data)
{
return MasterSlot()->TsPostProcess(Data);
}
void cMtdCamSlot::InjectEit(int Sid)
{
MasterSlot()->InjectEit(mtdMapper->RealToUniqSid(Sid));
}
int cMtdCamSlot::PutData(const uchar *Data, int Count)
{
int Free = mtdBuffer->Free();
Free -= Free % TS_SIZE;
if (Free < TS_SIZE)
return 0;
if (Free < Count)
Count = Free;
return mtdBuffer->Put(Data, Count);
}
int cMtdCamSlot::PutCat(const uchar *Data, int Count)
{
MasterSlot()->Decrypt(const_cast<uchar *>(Data), Count);
return Count;
}