mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Extended the CI API to allow plugins to implement additional CAM resources
This commit is contained in:
parent
50211c706a
commit
b01a1ee818
1
HISTORY
1
HISTORY
@ -9025,3 +9025,4 @@ Video Disk Recorder Revision History
|
|||||||
- Added handling RI_HOST_CONTROL to the CI protocol (no actual processing, but its
|
- Added handling RI_HOST_CONTROL to the CI protocol (no actual processing, but its
|
||||||
presence is required by some CAMs).
|
presence is required by some CAMs).
|
||||||
- Fixed a crash in case the bottom text of a CAM menu is empty.
|
- Fixed a crash in case the bottom text of a CAM menu is empty.
|
||||||
|
- Extended the CI API to allow plugins to implement additional CAM resources.
|
||||||
|
286
ci.c
286
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.15 2017/05/12 09:13:10 kls Exp $
|
* $Id: ci.c 4.16 2017/05/18 09:05:46 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ci.h"
|
#include "ci.h"
|
||||||
@ -292,7 +292,7 @@ void cCaActivationReceiver::Receive(const uchar *Data, int Length)
|
|||||||
|
|
||||||
// --- cTPDU -----------------------------------------------------------------
|
// --- cTPDU -----------------------------------------------------------------
|
||||||
|
|
||||||
#define MAX_TPDU_SIZE 2048
|
#define MAX_TPDU_SIZE 4096
|
||||||
#define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
|
#define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
|
||||||
|
|
||||||
#define DATA_INDICATOR 0x80
|
#define DATA_INDICATOR 0x80
|
||||||
@ -427,6 +427,7 @@ private:
|
|||||||
cTimeMs alive;
|
cTimeMs alive;
|
||||||
cTimeMs timer;
|
cTimeMs timer;
|
||||||
cCiSession *sessions[MAX_SESSIONS_PER_TC + 1]; // session numbering starts with 1
|
cCiSession *sessions[MAX_SESSIONS_PER_TC + 1]; // session numbering starts with 1
|
||||||
|
cCiSession *tsPostProcessor;
|
||||||
void SendTPDU(uint8_t Tag, int Length = 0, const uint8_t *Data = NULL);
|
void SendTPDU(uint8_t Tag, int Length = 0, const uint8_t *Data = NULL);
|
||||||
void SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId = 0, int Status = -1);
|
void SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId = 0, int Status = -1);
|
||||||
void Poll(void);
|
void Poll(void);
|
||||||
@ -438,6 +439,8 @@ private:
|
|||||||
public:
|
public:
|
||||||
cCiTransportConnection(cCamSlot *CamSlot, uint8_t Tcid);
|
cCiTransportConnection(cCamSlot *CamSlot, uint8_t Tcid);
|
||||||
virtual ~cCiTransportConnection();
|
virtual ~cCiTransportConnection();
|
||||||
|
void SetTsPostProcessor(cCiSession *CiSession);
|
||||||
|
bool TsPostProcess(uint8_t *TsPacket);
|
||||||
cCamSlot *CamSlot(void) { return camSlot; }
|
cCamSlot *CamSlot(void) { return camSlot; }
|
||||||
uint8_t Tcid(void) const { return tcid; }
|
uint8_t Tcid(void) const { return tcid; }
|
||||||
void CreateConnection(void) { createConnectionRequested = true; }
|
void CreateConnection(void) { createConnectionRequested = true; }
|
||||||
@ -527,24 +530,7 @@ public:
|
|||||||
#define AOT_COMMS_RCV_LAST 0x9F8C05
|
#define AOT_COMMS_RCV_LAST 0x9F8C05
|
||||||
#define AOT_COMMS_RCV_MORE 0x9F8C06
|
#define AOT_COMMS_RCV_MORE 0x9F8C06
|
||||||
|
|
||||||
class cCiSession {
|
#define RESOURCE_CLASS_MASK 0xFFFF0000
|
||||||
private:
|
|
||||||
uint16_t sessionId;
|
|
||||||
uint32_t resourceId;
|
|
||||||
cCiTransportConnection *tc;
|
|
||||||
protected:
|
|
||||||
int GetTag(int &Length, const uint8_t **Data);
|
|
||||||
const uint8_t *GetData(const uint8_t *Data, int &Length);
|
|
||||||
void SendData(int Tag, int Length = 0, const uint8_t *Data = NULL);
|
|
||||||
cCiTransportConnection *Tc(void) { return tc; }
|
|
||||||
public:
|
|
||||||
cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc);
|
|
||||||
virtual ~cCiSession();
|
|
||||||
uint16_t SessionId(void) { return sessionId; }
|
|
||||||
uint32_t ResourceId(void) { return resourceId; }
|
|
||||||
virtual bool HasUserIO(void) { return false; }
|
|
||||||
virtual void Process(int Length = 0, const uint8_t *Data = NULL);
|
|
||||||
};
|
|
||||||
|
|
||||||
cCiSession::cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc)
|
cCiSession::cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc)
|
||||||
{
|
{
|
||||||
@ -557,6 +543,16 @@ cCiSession::~cCiSession()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cCiSession::SetResourceId(uint32_t Id)
|
||||||
|
{
|
||||||
|
resourceId = Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cCiSession::SetTsPostProcessor(void)
|
||||||
|
{
|
||||||
|
tc->SetTsPostProcessor(this);
|
||||||
|
}
|
||||||
|
|
||||||
int cCiSession::GetTag(int &Length, const uint8_t **Data)
|
int cCiSession::GetTag(int &Length, const uint8_t **Data)
|
||||||
///< Gets the tag at Data.
|
///< Gets the tag at Data.
|
||||||
///< Returns the actual tag, or AOT_NONE in case of error.
|
///< Returns the actual tag, or AOT_NONE in case of error.
|
||||||
@ -580,7 +576,7 @@ const uint8_t *cCiSession::GetData(const uint8_t *Data, int &Length)
|
|||||||
|
|
||||||
void cCiSession::SendData(int Tag, int Length, const uint8_t *Data)
|
void cCiSession::SendData(int Tag, int Length, const uint8_t *Data)
|
||||||
{
|
{
|
||||||
uint8_t buffer[2048];
|
uint8_t buffer[MAX_TPDU_SIZE];
|
||||||
uint8_t *p = buffer;
|
uint8_t *p = buffer;
|
||||||
*p++ = ST_SESSION_NUMBER;
|
*p++ = ST_SESSION_NUMBER;
|
||||||
*p++ = 0x02;
|
*p++ = 0x02;
|
||||||
@ -597,7 +593,12 @@ void cCiSession::SendData(int Tag, int Length, const uint8_t *Data)
|
|||||||
tc->SendData(p - buffer, buffer);
|
tc->SendData(p - buffer, buffer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
esyslog("ERROR: CAM %d: data length (%d) exceeds buffer size", Tc()->CamSlot()->SlotNumber(), Length);
|
esyslog("ERROR: CAM %d: data length (%d) exceeds buffer size", CamSlot()->SlotNumber(), Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
cCamSlot *cCiSession::CamSlot(void)
|
||||||
|
{
|
||||||
|
return Tc()->CamSlot();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cCiSession::Process(int Length, const uint8_t *Data)
|
void cCiSession::Process(int Length, const uint8_t *Data)
|
||||||
@ -617,7 +618,7 @@ public:
|
|||||||
cCiResourceManager::cCiResourceManager(uint16_t SessionId, cCiTransportConnection *Tc)
|
cCiResourceManager::cCiResourceManager(uint16_t SessionId, cCiTransportConnection *Tc)
|
||||||
:cCiSession(SessionId, RI_RESOURCE_MANAGER, Tc)
|
:cCiSession(SessionId, RI_RESOURCE_MANAGER, Tc)
|
||||||
{
|
{
|
||||||
dbgprotocol("Slot %d: new Resource Manager (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
|
dbgprotocol("Slot %d: new Resource Manager (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
|
||||||
state = 0;
|
state = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -627,40 +628,33 @@ void cCiResourceManager::Process(int Length, const uint8_t *Data)
|
|||||||
int Tag = GetTag(Length, &Data);
|
int Tag = GetTag(Length, &Data);
|
||||||
switch (Tag) {
|
switch (Tag) {
|
||||||
case AOT_PROFILE_ENQ: {
|
case AOT_PROFILE_ENQ: {
|
||||||
dbgprotocol("Slot %d: <== Profile Enquiry (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: <== Profile Enquiry (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
uint32_t resources[] = { htonl(RI_RESOURCE_MANAGER),
|
dbgprotocol("Slot %d: ==> Profile (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
htonl(RI_APPLICATION_INFORMATION),
|
SendData(AOT_PROFILE, CiResourceHandlers.NumIds() * sizeof(uint32_t), (uint8_t*)CiResourceHandlers.Ids());
|
||||||
htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
|
|
||||||
htonl(RI_HOST_CONTROL),
|
|
||||||
htonl(RI_DATE_TIME),
|
|
||||||
htonl(RI_MMI)
|
|
||||||
};
|
|
||||||
dbgprotocol("Slot %d: ==> Profile (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
|
||||||
SendData(AOT_PROFILE, sizeof(resources), (uint8_t*)resources);
|
|
||||||
state = 3;
|
state = 3;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AOT_PROFILE: {
|
case AOT_PROFILE: {
|
||||||
dbgprotocol("Slot %d: <== Profile (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: <== Profile (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
if (state == 1) {
|
if (state == 1) {
|
||||||
int l = 0;
|
int l = 0;
|
||||||
const uint8_t *d = GetData(Data, l);
|
const uint8_t *d = GetData(Data, l);
|
||||||
if (l > 0 && d)
|
if (l > 0 && d)
|
||||||
esyslog("ERROR: CAM %d: resource manager: unexpected data", Tc()->CamSlot()->SlotNumber());
|
esyslog("ERROR: CAM %d: resource manager: unexpected data", CamSlot()->SlotNumber());
|
||||||
dbgprotocol("Slot %d: ==> Profile Change (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: ==> Profile Change (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
SendData(AOT_PROFILE_CHANGE);
|
SendData(AOT_PROFILE_CHANGE);
|
||||||
state = 2;
|
state = 2;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
esyslog("ERROR: CAM %d: resource manager: unexpected tag %06X in state %d", Tc()->CamSlot()->SlotNumber(), Tag, state);
|
esyslog("ERROR: CAM %d: resource manager: unexpected tag %06X in state %d", CamSlot()->SlotNumber(), Tag, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: esyslog("ERROR: CAM %d: resource manager: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
|
default: esyslog("ERROR: CAM %d: resource manager: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (state == 0) {
|
else if (state == 0) {
|
||||||
dbgprotocol("Slot %d: ==> Profile Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: ==> Profile Enq (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
SendData(AOT_PROFILE_ENQ);
|
SendData(AOT_PROFILE_ENQ);
|
||||||
state = 1;
|
state = 1;
|
||||||
}
|
}
|
||||||
@ -668,25 +662,10 @@ void cCiResourceManager::Process(int Length, const uint8_t *Data)
|
|||||||
|
|
||||||
// --- cCiApplicationInformation ---------------------------------------------
|
// --- cCiApplicationInformation ---------------------------------------------
|
||||||
|
|
||||||
class cCiApplicationInformation : public cCiSession {
|
|
||||||
private:
|
|
||||||
int state;
|
|
||||||
uint8_t applicationType;
|
|
||||||
uint16_t applicationManufacturer;
|
|
||||||
uint16_t manufacturerCode;
|
|
||||||
char *menuString;
|
|
||||||
public:
|
|
||||||
cCiApplicationInformation(uint16_t SessionId, cCiTransportConnection *Tc);
|
|
||||||
virtual ~cCiApplicationInformation();
|
|
||||||
virtual void Process(int Length = 0, const uint8_t *Data = NULL);
|
|
||||||
bool EnterMenu(void);
|
|
||||||
const char *GetMenuString(void) { return menuString; }
|
|
||||||
};
|
|
||||||
|
|
||||||
cCiApplicationInformation::cCiApplicationInformation(uint16_t SessionId, cCiTransportConnection *Tc)
|
cCiApplicationInformation::cCiApplicationInformation(uint16_t SessionId, cCiTransportConnection *Tc)
|
||||||
:cCiSession(SessionId, RI_APPLICATION_INFORMATION, Tc)
|
:cCiSession(SessionId, RI_APPLICATION_INFORMATION, Tc)
|
||||||
{
|
{
|
||||||
dbgprotocol("Slot %d: new Application Information (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
|
dbgprotocol("Slot %d: new Application Information (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
|
||||||
state = 0;
|
state = 0;
|
||||||
menuString = NULL;
|
menuString = NULL;
|
||||||
}
|
}
|
||||||
@ -702,7 +681,7 @@ void cCiApplicationInformation::Process(int Length, const uint8_t *Data)
|
|||||||
int Tag = GetTag(Length, &Data);
|
int Tag = GetTag(Length, &Data);
|
||||||
switch (Tag) {
|
switch (Tag) {
|
||||||
case AOT_APPLICATION_INFO: {
|
case AOT_APPLICATION_INFO: {
|
||||||
dbgprotocol("Slot %d: <== Application Info (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: <== Application Info (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
int l = 0;
|
int l = 0;
|
||||||
const uint8_t *d = GetData(Data, l);
|
const uint8_t *d = GetData(Data, l);
|
||||||
if ((l -= 1) < 0) break;
|
if ((l -= 1) < 0) break;
|
||||||
@ -715,15 +694,15 @@ void cCiApplicationInformation::Process(int Length, const uint8_t *Data)
|
|||||||
d += 2;
|
d += 2;
|
||||||
free(menuString);
|
free(menuString);
|
||||||
menuString = GetString(l, &d);
|
menuString = GetString(l, &d);
|
||||||
isyslog("CAM %d: %s, %02X, %04X, %04X", Tc()->CamSlot()->SlotNumber(), menuString, applicationType, applicationManufacturer, manufacturerCode);
|
isyslog("CAM %d: %s, %02X, %04X, %04X", CamSlot()->SlotNumber(), menuString, applicationType, applicationManufacturer, manufacturerCode);
|
||||||
state = 2;
|
state = 2;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: esyslog("ERROR: CAM %d: application information: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
|
default: esyslog("ERROR: CAM %d: application information: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (state == 0) {
|
else if (state == 0) {
|
||||||
dbgprotocol("Slot %d: ==> Application Info Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: ==> Application Info Enq (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
SendData(AOT_APPLICATION_INFO_ENQ);
|
SendData(AOT_APPLICATION_INFO_ENQ);
|
||||||
state = 1;
|
state = 1;
|
||||||
}
|
}
|
||||||
@ -732,7 +711,7 @@ void cCiApplicationInformation::Process(int Length, const uint8_t *Data)
|
|||||||
bool cCiApplicationInformation::EnterMenu(void)
|
bool cCiApplicationInformation::EnterMenu(void)
|
||||||
{
|
{
|
||||||
if (state == 2) {
|
if (state == 2) {
|
||||||
dbgprotocol("Slot %d: ==> Enter Menu (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: ==> Enter Menu (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
SendData(AOT_ENTER_MENU);
|
SendData(AOT_ENTER_MENU);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -954,7 +933,7 @@ public:
|
|||||||
cCiConditionalAccessSupport::cCiConditionalAccessSupport(uint16_t SessionId, cCiTransportConnection *Tc)
|
cCiConditionalAccessSupport::cCiConditionalAccessSupport(uint16_t SessionId, cCiTransportConnection *Tc)
|
||||||
:cCiSession(SessionId, RI_CONDITIONAL_ACCESS_SUPPORT, Tc)
|
:cCiSession(SessionId, RI_CONDITIONAL_ACCESS_SUPPORT, Tc)
|
||||||
{
|
{
|
||||||
dbgprotocol("Slot %d: new Conditional Access Support (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
|
dbgprotocol("Slot %d: new Conditional Access Support (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
|
||||||
state = 0; // inactive
|
state = 0; // inactive
|
||||||
caSystemIds[numCaSystemIds = 0] = 0;
|
caSystemIds[numCaSystemIds = 0] = 0;
|
||||||
repliesToQuery = false;
|
repliesToQuery = false;
|
||||||
@ -966,7 +945,7 @@ void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
|
|||||||
int Tag = GetTag(Length, &Data);
|
int Tag = GetTag(Length, &Data);
|
||||||
switch (Tag) {
|
switch (Tag) {
|
||||||
case AOT_CA_INFO: {
|
case AOT_CA_INFO: {
|
||||||
dbgprotocol("Slot %d: <== Ca Info (%d)", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: <== Ca Info (%d)", CamSlot()->SlotNumber(), SessionId());
|
||||||
cString Ids;
|
cString Ids;
|
||||||
numCaSystemIds = 0;
|
numCaSystemIds = 0;
|
||||||
int l = 0;
|
int l = 0;
|
||||||
@ -980,7 +959,7 @@ void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
|
|||||||
if (numCaSystemIds < MAXCASYSTEMIDS)
|
if (numCaSystemIds < MAXCASYSTEMIDS)
|
||||||
caSystemIds[numCaSystemIds++] = id;
|
caSystemIds[numCaSystemIds++] = id;
|
||||||
else {
|
else {
|
||||||
esyslog("ERROR: CAM %d: too many CA system IDs!", Tc()->CamSlot()->SlotNumber());
|
esyslog("ERROR: CAM %d: too many CA system IDs!", CamSlot()->SlotNumber());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -990,17 +969,17 @@ void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
|
|||||||
timer.Set(QUERY_WAIT_TIME); // WORKAROUND: Alphacrypt 3.09 doesn't reply to QUERY immediately after reset
|
timer.Set(QUERY_WAIT_TIME); // WORKAROUND: Alphacrypt 3.09 doesn't reply to QUERY immediately after reset
|
||||||
state = 2; // got ca info
|
state = 2; // got ca info
|
||||||
}
|
}
|
||||||
dsyslog("CAM %d: system ids:%s", Tc()->CamSlot()->SlotNumber(), *Ids ? *Ids : " none");
|
dsyslog("CAM %d: system ids:%s", CamSlot()->SlotNumber(), *Ids ? *Ids : " none");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
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)", CamSlot()->SlotNumber(), SessionId());
|
||||||
if (!repliesToQuery) {
|
if (!repliesToQuery) {
|
||||||
dsyslog("CAM %d: replies to QUERY - multi channel decryption (MCD) possible", Tc()->CamSlot()->SlotNumber());
|
dsyslog("CAM %d: replies to QUERY - multi channel decryption (MCD) possible", CamSlot()->SlotNumber());
|
||||||
repliesToQuery = true;
|
repliesToQuery = true;
|
||||||
if (Tc()->CamSlot()->MtdAvailable()) {
|
if (CamSlot()->MtdAvailable()) {
|
||||||
dsyslog("CAM %d: supports multi transponder decryption (MTD)", Tc()->CamSlot()->SlotNumber());
|
dsyslog("CAM %d: supports multi transponder decryption (MTD)", CamSlot()->SlotNumber());
|
||||||
Tc()->CamSlot()->MtdActivate(true);
|
CamSlot()->MtdActivate(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state = 5; // got ca pmt reply
|
state = 5; // got ca pmt reply
|
||||||
@ -1052,11 +1031,11 @@ void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
|
|||||||
dbgprotocol("\n");
|
dbgprotocol("\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: esyslog("ERROR: CAM %d: conditional access support: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
|
default: esyslog("ERROR: CAM %d: conditional access support: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (state == 0) {
|
else if (state == 0) {
|
||||||
dbgprotocol("Slot %d: ==> Ca Info Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: ==> Ca Info Enq (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
SendData(AOT_CA_INFO_ENQ);
|
SendData(AOT_CA_INFO_ENQ);
|
||||||
state = 1; // enquired ca info
|
state = 1; // enquired ca info
|
||||||
}
|
}
|
||||||
@ -1067,7 +1046,7 @@ void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
|
|||||||
state = 3; // waiting for reply
|
state = 3; // waiting for reply
|
||||||
}
|
}
|
||||||
else if (state == 3 && timer.TimedOut()) {
|
else if (state == 3 && timer.TimedOut()) {
|
||||||
dsyslog("CAM %d: doesn't reply to QUERY - only a single channel can be decrypted", Tc()->CamSlot()->SlotNumber());
|
dsyslog("CAM %d: doesn't reply to QUERY - only a single channel can be decrypted", CamSlot()->SlotNumber());
|
||||||
state = 4; // normal operation
|
state = 4; // normal operation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1075,7 +1054,7 @@ void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
|
|||||||
void cCiConditionalAccessSupport::SendPMT(cCiCaPmt *CaPmt)
|
void cCiConditionalAccessSupport::SendPMT(cCiCaPmt *CaPmt)
|
||||||
{
|
{
|
||||||
if (CaPmt && state >= 2) {
|
if (CaPmt && state >= 2) {
|
||||||
dbgprotocol("Slot %d: ==> Ca Pmt (%d) %d %d\n", Tc()->CamSlot()->SlotNumber(), SessionId(), CaPmt->ListManagement(), CaPmt->CmdId());
|
dbgprotocol("Slot %d: ==> Ca Pmt (%d) %d %d\n", CamSlot()->SlotNumber(), SessionId(), CaPmt->ListManagement(), CaPmt->CmdId());
|
||||||
SendData(AOT_CA_PMT, CaPmt->capmt.Length(), CaPmt->capmt.Data());
|
SendData(AOT_CA_PMT, CaPmt->capmt.Length(), CaPmt->capmt.Data());
|
||||||
state = 4; // sent ca pmt
|
state = 4; // sent ca pmt
|
||||||
}
|
}
|
||||||
@ -1092,7 +1071,7 @@ public:
|
|||||||
cCiHostControl::cCiHostControl(uint16_t SessionId, cCiTransportConnection* Tc)
|
cCiHostControl::cCiHostControl(uint16_t SessionId, cCiTransportConnection* Tc)
|
||||||
:cCiSession(SessionId, RI_HOST_CONTROL, Tc)
|
:cCiSession(SessionId, RI_HOST_CONTROL, Tc)
|
||||||
{
|
{
|
||||||
dbgprotocol("Slot %d: new Host Control (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
|
dbgprotocol("Slot %d: new Host Control (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cCiHostControl::Process(int Length, const uint8_t* Data)
|
void cCiHostControl::Process(int Length, const uint8_t* Data)
|
||||||
@ -1101,15 +1080,15 @@ void cCiHostControl::Process(int Length, const uint8_t* Data)
|
|||||||
int Tag = GetTag(Length, &Data);
|
int Tag = GetTag(Length, &Data);
|
||||||
switch (Tag) {
|
switch (Tag) {
|
||||||
case AOT_TUNE:
|
case AOT_TUNE:
|
||||||
dbgprotocol("Slot %d: <== Host Control Tune (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: <== Host Control Tune (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
break;
|
break;
|
||||||
case AOT_REPLACE:
|
case AOT_REPLACE:
|
||||||
dbgprotocol("Slot %d: <== Host Control Replace (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: <== Host Control Replace (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
break;
|
break;
|
||||||
case AOT_CLEAR_REPLACE:
|
case AOT_CLEAR_REPLACE:
|
||||||
dbgprotocol("Slot %d: <== Host Control Clear Replace (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: <== Host Control Clear Replace (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
break;
|
break;
|
||||||
default: esyslog("ERROR: CAM %d: Host Control: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
|
default: esyslog("ERROR: CAM %d: Host Control: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1131,7 +1110,7 @@ cCiDateTime::cCiDateTime(uint16_t SessionId, cCiTransportConnection *Tc)
|
|||||||
{
|
{
|
||||||
interval = 0;
|
interval = 0;
|
||||||
lastTime = 0;
|
lastTime = 0;
|
||||||
dbgprotocol("Slot %d: new Date Time (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
|
dbgprotocol("Slot %d: new Date Time (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cCiDateTime::SendDateTime(void)
|
void cCiDateTime::SendDateTime(void)
|
||||||
@ -1153,7 +1132,7 @@ void cCiDateTime::SendDateTime(void)
|
|||||||
bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer;
|
bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer;
|
||||||
DumpTPDUDataTransfer &= DumpDateTime;
|
DumpTPDUDataTransfer &= DumpDateTime;
|
||||||
if (DumpDateTime)
|
if (DumpDateTime)
|
||||||
dbgprotocol("Slot %d: ==> Date Time (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: ==> Date Time (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
SendData(AOT_DATE_TIME, 7, (uint8_t*)&T);
|
SendData(AOT_DATE_TIME, 7, (uint8_t*)&T);
|
||||||
DumpTPDUDataTransfer = OldDumpTPDUDataTransfer;
|
DumpTPDUDataTransfer = OldDumpTPDUDataTransfer;
|
||||||
}
|
}
|
||||||
@ -1170,12 +1149,12 @@ void cCiDateTime::Process(int Length, const uint8_t *Data)
|
|||||||
const uint8_t *d = GetData(Data, l);
|
const uint8_t *d = GetData(Data, l);
|
||||||
if (l > 0)
|
if (l > 0)
|
||||||
interval = *d;
|
interval = *d;
|
||||||
dbgprotocol("Slot %d: <== Date Time Enq (%d), interval = %d\n", Tc()->CamSlot()->SlotNumber(), SessionId(), interval);
|
dbgprotocol("Slot %d: <== Date Time Enq (%d), interval = %d\n", CamSlot()->SlotNumber(), SessionId(), interval);
|
||||||
lastTime = time(NULL);
|
lastTime = time(NULL);
|
||||||
SendDateTime();
|
SendDateTime();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: esyslog("ERROR: CAM %d: date time: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
|
default: esyslog("ERROR: CAM %d: date time: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (interval && time(NULL) - lastTime > interval) {
|
else if (interval && time(NULL) - lastTime > interval) {
|
||||||
@ -1240,7 +1219,7 @@ public:
|
|||||||
cCiMMI::cCiMMI(uint16_t SessionId, cCiTransportConnection *Tc)
|
cCiMMI::cCiMMI(uint16_t SessionId, cCiTransportConnection *Tc)
|
||||||
:cCiSession(SessionId, RI_MMI, Tc)
|
:cCiSession(SessionId, RI_MMI, Tc)
|
||||||
{
|
{
|
||||||
dbgprotocol("Slot %d: new MMI (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
|
dbgprotocol("Slot %d: new MMI (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
|
||||||
menu = fetchedMenu = NULL;
|
menu = fetchedMenu = NULL;
|
||||||
enquiry = fetchedEnquiry = NULL;
|
enquiry = fetchedEnquiry = NULL;
|
||||||
}
|
}
|
||||||
@ -1267,11 +1246,11 @@ char *cCiMMI::GetText(int &Length, const uint8_t **Data)
|
|||||||
int Tag = GetTag(Length, Data);
|
int Tag = GetTag(Length, Data);
|
||||||
if (Tag == AOT_TEXT_LAST) {
|
if (Tag == AOT_TEXT_LAST) {
|
||||||
char *s = GetString(Length, Data);
|
char *s = GetString(Length, Data);
|
||||||
dbgprotocol("Slot %d: <== Text Last (%d) '%s'\n", Tc()->CamSlot()->SlotNumber(), SessionId(), s);
|
dbgprotocol("Slot %d: <== Text Last (%d) '%s'\n", CamSlot()->SlotNumber(), SessionId(), s);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
esyslog("ERROR: CAM %d: MMI: unexpected text tag: %06X", Tc()->CamSlot()->SlotNumber(), Tag);
|
esyslog("ERROR: CAM %d: MMI: unexpected text tag: %06X", CamSlot()->SlotNumber(), Tag);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1281,7 +1260,7 @@ void cCiMMI::Process(int Length, const uint8_t *Data)
|
|||||||
int Tag = GetTag(Length, &Data);
|
int Tag = GetTag(Length, &Data);
|
||||||
switch (Tag) {
|
switch (Tag) {
|
||||||
case AOT_DISPLAY_CONTROL: {
|
case AOT_DISPLAY_CONTROL: {
|
||||||
dbgprotocol("Slot %d: <== Display Control (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: <== Display Control (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
int l = 0;
|
int l = 0;
|
||||||
const uint8_t *d = GetData(Data, l);
|
const uint8_t *d = GetData(Data, l);
|
||||||
if (l > 0) {
|
if (l > 0) {
|
||||||
@ -1290,18 +1269,18 @@ void cCiMMI::Process(int Length, const uint8_t *Data)
|
|||||||
if (l == 2 && *++d == MM_HIGH_LEVEL) {
|
if (l == 2 && *++d == MM_HIGH_LEVEL) {
|
||||||
struct tDisplayReply { uint8_t id; uint8_t mode; };
|
struct tDisplayReply { uint8_t id; uint8_t mode; };
|
||||||
tDisplayReply dr = { id : DRI_MMI_MODE_ACK, mode : MM_HIGH_LEVEL };
|
tDisplayReply dr = { id : DRI_MMI_MODE_ACK, mode : MM_HIGH_LEVEL };
|
||||||
dbgprotocol("Slot %d: ==> Display Reply (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: ==> Display Reply (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
SendData(AOT_DISPLAY_REPLY, 2, (uint8_t *)&dr);
|
SendData(AOT_DISPLAY_REPLY, 2, (uint8_t *)&dr);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: esyslog("ERROR: CAM %d: MMI: unsupported display control command %02X", Tc()->CamSlot()->SlotNumber(), *d);
|
default: esyslog("ERROR: CAM %d: MMI: unsupported display control command %02X", CamSlot()->SlotNumber(), *d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AOT_LIST_LAST:
|
case AOT_LIST_LAST:
|
||||||
case AOT_MENU_LAST: {
|
case AOT_MENU_LAST: {
|
||||||
dbgprotocol("Slot %d: <== Menu Last (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: <== Menu Last (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
delete menu;
|
delete menu;
|
||||||
menu = new cCiMenu(this, Tag == AOT_MENU_LAST);
|
menu = new cCiMenu(this, Tag == AOT_MENU_LAST);
|
||||||
int l = 0;
|
int l = 0;
|
||||||
@ -1326,7 +1305,7 @@ void cCiMMI::Process(int Length, const uint8_t *Data)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AOT_ENQ: {
|
case AOT_ENQ: {
|
||||||
dbgprotocol("Slot %d: <== Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: <== Enq (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
delete enquiry;
|
delete enquiry;
|
||||||
enquiry = new cCiEnquiry(this);
|
enquiry = new cCiEnquiry(this);
|
||||||
int l = 0;
|
int l = 0;
|
||||||
@ -1353,10 +1332,10 @@ void cCiMMI::Process(int Length, const uint8_t *Data)
|
|||||||
if (l > 1)
|
if (l > 1)
|
||||||
delay = *d;
|
delay = *d;
|
||||||
}
|
}
|
||||||
dbgprotocol("Slot %d: <== Close MMI (%d) id = %02X delay = %d\n", Tc()->CamSlot()->SlotNumber(), SessionId(), id, delay);
|
dbgprotocol("Slot %d: <== Close MMI (%d) id = %02X delay = %d\n", CamSlot()->SlotNumber(), SessionId(), id, delay);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: esyslog("ERROR: CAM %d: MMI: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
|
default: esyslog("ERROR: CAM %d: MMI: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1385,13 +1364,13 @@ cCiEnquiry *cCiMMI::Enquiry(bool Clear)
|
|||||||
|
|
||||||
void cCiMMI::SendMenuAnswer(uint8_t Selection)
|
void cCiMMI::SendMenuAnswer(uint8_t Selection)
|
||||||
{
|
{
|
||||||
dbgprotocol("Slot %d: ==> Menu Answ (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: ==> Menu Answ (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
SendData(AOT_MENU_ANSW, 1, &Selection);
|
SendData(AOT_MENU_ANSW, 1, &Selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cCiMMI::SendAnswer(const char *Text)
|
bool cCiMMI::SendAnswer(const char *Text)
|
||||||
{
|
{
|
||||||
dbgprotocol("Slot %d: ==> Answ (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: ==> Answ (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
struct tAnswer { uint8_t id; char text[256]; };//XXX
|
struct tAnswer { uint8_t id; char text[256]; };//XXX
|
||||||
tAnswer answer;
|
tAnswer answer;
|
||||||
answer.id = Text ? AI_ANSWER : AI_CANCEL;
|
answer.id = Text ? AI_ANSWER : AI_CANCEL;
|
||||||
@ -1403,7 +1382,7 @@ bool cCiMMI::SendAnswer(const char *Text)
|
|||||||
|
|
||||||
bool cCiMMI::SendCloseMMI(void)
|
bool cCiMMI::SendCloseMMI(void)
|
||||||
{
|
{
|
||||||
dbgprotocol("Slot %d: ==> Close MMI (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
|
dbgprotocol("Slot %d: ==> Close MMI (%d)\n", CamSlot()->SlotNumber(), SessionId());
|
||||||
SendData(AOT_CLOSE_MMI, 0);
|
SendData(AOT_CLOSE_MMI, 0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1502,6 +1481,82 @@ void cCiEnquiry::Abort(void)
|
|||||||
mmi->SendCloseMMI();
|
mmi->SendCloseMMI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- cCiResourceHandler ----------------------------------------------------
|
||||||
|
|
||||||
|
cCiResourceHandler::cCiResourceHandler(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
cCiResourceHandler::~cCiResourceHandler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cCiDefaultResourceHandler ---------------------------------------------
|
||||||
|
|
||||||
|
class cCiDefaultResourceHandler : public cCiResourceHandler {
|
||||||
|
public:
|
||||||
|
virtual const uint32_t *ResourceIds(void) const;
|
||||||
|
virtual cCiSession *GetNewCiSession(uint32_t ResourceId, uint16_t SessionId, cCiTransportConnection *Tc);
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint32_t *cCiDefaultResourceHandler::ResourceIds(void) const
|
||||||
|
{
|
||||||
|
static uint32_t Ids[] = {
|
||||||
|
RI_RESOURCE_MANAGER,
|
||||||
|
RI_APPLICATION_INFORMATION,
|
||||||
|
RI_CONDITIONAL_ACCESS_SUPPORT,
|
||||||
|
RI_HOST_CONTROL,
|
||||||
|
RI_DATE_TIME,
|
||||||
|
RI_MMI,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
return Ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
cCiSession *cCiDefaultResourceHandler::GetNewCiSession(uint32_t ResourceId, uint16_t SessionId, cCiTransportConnection *Tc)
|
||||||
|
{
|
||||||
|
switch (ResourceId) {
|
||||||
|
case RI_RESOURCE_MANAGER: return new cCiResourceManager(SessionId, Tc); break;
|
||||||
|
case RI_APPLICATION_INFORMATION: return new cCiApplicationInformation(SessionId, Tc); break;
|
||||||
|
case RI_CONDITIONAL_ACCESS_SUPPORT: return new cCiConditionalAccessSupport(SessionId, Tc); break;
|
||||||
|
case RI_HOST_CONTROL: return new cCiHostControl(SessionId, Tc); break;
|
||||||
|
case RI_DATE_TIME: return new cCiDateTime(SessionId, Tc); break;
|
||||||
|
case RI_MMI: return new cCiMMI(SessionId, Tc); break;
|
||||||
|
default: return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cCiResourceHandlers ---------------------------------------------------
|
||||||
|
|
||||||
|
cCiResourceHandlers CiResourceHandlers;
|
||||||
|
|
||||||
|
cCiResourceHandlers::cCiResourceHandlers(void)
|
||||||
|
{
|
||||||
|
Register(new cCiDefaultResourceHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cCiResourceHandlers::Register(cCiResourceHandler *ResourceHandler)
|
||||||
|
{
|
||||||
|
if (ResourceHandler) {
|
||||||
|
Add(ResourceHandler);
|
||||||
|
if (const uint32_t *r = ResourceHandler->ResourceIds()) {
|
||||||
|
while (*r) {
|
||||||
|
resourceIds.Append(htonl(*r));
|
||||||
|
r++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cCiSession *cCiResourceHandlers::GetNewCiSession(uint32_t ResourceId, uint16_t SessionId, cCiTransportConnection *Tc)
|
||||||
|
{
|
||||||
|
for (cCiResourceHandler *r = Last(); r; r = Prev(r)) {
|
||||||
|
if (cCiSession *CiSession = r->GetNewCiSession(ResourceId, SessionId, Tc))
|
||||||
|
return CiSession;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// --- cCiTransportConnection (cont'd) ---------------------------------------
|
// --- cCiTransportConnection (cont'd) ---------------------------------------
|
||||||
|
|
||||||
#define TC_POLL_TIMEOUT 300 // ms WORKAROUND: TC_POLL_TIMEOUT < 300ms doesn't work with DragonCAM
|
#define TC_POLL_TIMEOUT 300 // ms WORKAROUND: TC_POLL_TIMEOUT < 300ms doesn't work with DragonCAM
|
||||||
@ -1519,6 +1574,7 @@ cCiTransportConnection::cCiTransportConnection(cCamSlot *CamSlot, uint8_t Tcid)
|
|||||||
alive.Set(TC_ALIVE_TIMEOUT);
|
alive.Set(TC_ALIVE_TIMEOUT);
|
||||||
for (int i = 0; i <= MAX_SESSIONS_PER_TC; i++) // sessions[0] is not used, but initialized anyway
|
for (int i = 0; i <= MAX_SESSIONS_PER_TC; i++) // sessions[0] is not used, but initialized anyway
|
||||||
sessions[i] = NULL;
|
sessions[i] = NULL;
|
||||||
|
tsPostProcessor = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
cCiTransportConnection::~cCiTransportConnection()
|
cCiTransportConnection::~cCiTransportConnection()
|
||||||
@ -1527,6 +1583,18 @@ cCiTransportConnection::~cCiTransportConnection()
|
|||||||
delete sessions[i];
|
delete sessions[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cCiTransportConnection::SetTsPostProcessor(cCiSession *CiSession)
|
||||||
|
{
|
||||||
|
tsPostProcessor = CiSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cCiTransportConnection::TsPostProcess(uint8_t *TsPacket)
|
||||||
|
{
|
||||||
|
if (tsPostProcessor)
|
||||||
|
return tsPostProcessor->TsPostProcess(TsPacket);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool cCiTransportConnection::Ready(void)
|
bool cCiTransportConnection::Ready(void)
|
||||||
{
|
{
|
||||||
cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
|
cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
|
||||||
@ -1593,11 +1661,16 @@ cCiSession *cCiTransportConnection::GetSessionBySessionId(uint16_t SessionId)
|
|||||||
|
|
||||||
cCiSession *cCiTransportConnection::GetSessionByResourceId(uint32_t ResourceId)
|
cCiSession *cCiTransportConnection::GetSessionByResourceId(uint32_t ResourceId)
|
||||||
{
|
{
|
||||||
|
cCiSession *CiSession = NULL;
|
||||||
for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
|
for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
|
||||||
if (sessions[i] && sessions[i]->ResourceId() == ResourceId)
|
if (cCiSession *s = sessions[i]) {
|
||||||
return sessions[i];
|
if (s->ResourceId() == ResourceId)
|
||||||
|
return s; // prefer exact match
|
||||||
|
if ((s->ResourceId() & RESOURCE_CLASS_MASK) == (ResourceId & RESOURCE_CLASS_MASK))
|
||||||
|
CiSession = s;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return CiSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cCiTransportConnection::OpenSession(int Length, const uint8_t *Data)
|
void cCiTransportConnection::OpenSession(int Length, const uint8_t *Data)
|
||||||
@ -1608,17 +1681,11 @@ void cCiTransportConnection::OpenSession(int Length, const uint8_t *Data)
|
|||||||
if (!GetSessionByResourceId(ResourceId)) {
|
if (!GetSessionByResourceId(ResourceId)) {
|
||||||
for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
|
for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
|
||||||
if (!sessions[i]) {
|
if (!sessions[i]) {
|
||||||
switch (ResourceId) {
|
sessions[i] = CiResourceHandlers.GetNewCiSession(ResourceId, i, this);
|
||||||
case RI_RESOURCE_MANAGER: sessions[i] = new cCiResourceManager(i, this); break;
|
|
||||||
case RI_APPLICATION_INFORMATION: sessions[i] = new cCiApplicationInformation(i, this); break;
|
|
||||||
case RI_CONDITIONAL_ACCESS_SUPPORT: sessions[i] = new cCiConditionalAccessSupport(i, this); break;
|
|
||||||
case RI_HOST_CONTROL: sessions[i] = new cCiHostControl(i, this); break;
|
|
||||||
case RI_DATE_TIME: sessions[i] = new cCiDateTime(i, this); break;
|
|
||||||
case RI_MMI: sessions[i] = new cCiMMI(i, this); break;
|
|
||||||
default: esyslog("ERROR: CAM %d: unknown resource identifier: %08X (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
|
|
||||||
}
|
|
||||||
if (sessions[i])
|
if (sessions[i])
|
||||||
SendTag(ST_OPEN_SESSION_RESPONSE, sessions[i]->SessionId(), sessions[i]->ResourceId(), SS_OK);
|
SendTag(ST_OPEN_SESSION_RESPONSE, sessions[i]->SessionId(), sessions[i]->ResourceId(), SS_OK);
|
||||||
|
else
|
||||||
|
esyslog("ERROR: CAM %d: unknown resource identifier: %08X (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2458,6 +2525,11 @@ uchar *cCamSlot::Decrypt(uchar *Data, int &Count)
|
|||||||
return Data;
|
return Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cCamSlot::TsPostProcess(uchar *Data)
|
||||||
|
{
|
||||||
|
return tc[1] ? tc[1]->TsPostProcess(Data) : false;
|
||||||
|
}
|
||||||
|
|
||||||
bool cCamSlot::Inject(uchar *Data, int Count)
|
bool cCamSlot::Inject(uchar *Data, int Count)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
102
ci.h
102
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.8 2017/05/01 16:26:34 kls Exp $
|
* $Id: ci.h 4.9 2017/05/18 09:05:46 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __CI_H
|
#ifndef __CI_H
|
||||||
@ -21,6 +21,99 @@
|
|||||||
#define MAX_CONNECTIONS_PER_CAM_SLOT 8 // maximum possible value is 254
|
#define MAX_CONNECTIONS_PER_CAM_SLOT 8 // maximum possible value is 254
|
||||||
#define CAM_READ_TIMEOUT 50 // ms
|
#define CAM_READ_TIMEOUT 50 // ms
|
||||||
|
|
||||||
|
class cCiTransportConnection;
|
||||||
|
class cCamSlot;
|
||||||
|
|
||||||
|
// VDR's Common Interface functions implement only the features that are absolutely
|
||||||
|
// necessary to control a CAM. If a plugin wants to implement additional functionality
|
||||||
|
// (i.e. "resources"), it can do so by deriving from cCiResourceHandler, cCiSession
|
||||||
|
// and (if necessary) from cCiApplicationInformation.
|
||||||
|
|
||||||
|
class cCiSession {
|
||||||
|
private:
|
||||||
|
uint16_t sessionId;
|
||||||
|
uint32_t resourceId;
|
||||||
|
cCiTransportConnection *tc;
|
||||||
|
protected:
|
||||||
|
void SetTsPostProcessor(void);
|
||||||
|
///< If this cCiSession implements the TsPostProcess() function, it shall call
|
||||||
|
///< SetTsPostProcessor() to register itself as the TS post processor.
|
||||||
|
void SetResourceId(uint32_t Id);
|
||||||
|
///< If this is a class that has been derived from an existing cCiSession class,
|
||||||
|
///< but implements a different resource id, it shall call SetResourceId() with
|
||||||
|
///< that Id.
|
||||||
|
int GetTag(int &Length, const uint8_t **Data);
|
||||||
|
const uint8_t *GetData(const uint8_t *Data, int &Length);
|
||||||
|
void SendData(int Tag, int Length = 0, const uint8_t *Data = NULL);
|
||||||
|
cCiTransportConnection *Tc(void) { return tc; }
|
||||||
|
public:
|
||||||
|
cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc);
|
||||||
|
virtual ~cCiSession();
|
||||||
|
uint16_t SessionId(void) { return sessionId; }
|
||||||
|
uint32_t ResourceId(void) { return resourceId; }
|
||||||
|
cCamSlot *CamSlot(void);
|
||||||
|
virtual bool HasUserIO(void) { return false; }
|
||||||
|
virtual void Process(int Length = 0, const uint8_t *Data = NULL);
|
||||||
|
virtual bool TsPostProcess(uint8_t *TsPacket) { return false; }
|
||||||
|
///< If this cCiSession needs to do additional processing on TS packets (after
|
||||||
|
///< the CAM has done the decryption), it shall implement TsPostProcess() and
|
||||||
|
///< do whatever operations are necessary on the given TsPacket. This function
|
||||||
|
///< is called once for each TS packet, and any and all operations must be
|
||||||
|
///< finished upon return.
|
||||||
|
///< A derived cCiSession that implements this function must call
|
||||||
|
///< SetTsPostProcessor() to make it actually get called.
|
||||||
|
///< Returns true if the TsPacket was in any way modified.
|
||||||
|
};
|
||||||
|
|
||||||
|
class cCiApplicationInformation : public cCiSession {
|
||||||
|
protected:
|
||||||
|
int state;
|
||||||
|
uint8_t applicationType;
|
||||||
|
uint16_t applicationManufacturer;
|
||||||
|
uint16_t manufacturerCode;
|
||||||
|
char *menuString;
|
||||||
|
public:
|
||||||
|
cCiApplicationInformation(uint16_t SessionId, cCiTransportConnection *Tc);
|
||||||
|
virtual ~cCiApplicationInformation();
|
||||||
|
virtual void Process(int Length = 0, const uint8_t *Data = NULL);
|
||||||
|
bool EnterMenu(void);
|
||||||
|
const char *GetMenuString(void) { return menuString; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class cCiResourceHandler : public cListObject {
|
||||||
|
public:
|
||||||
|
cCiResourceHandler(void);
|
||||||
|
///< Creates a new resource handler, through which the available resources
|
||||||
|
///< can be provides. A resource handler shall be allocated on the heap and
|
||||||
|
///< registered with the global CiResourceHandlers, as in
|
||||||
|
///< CiResourceHandlers.Register(new cMyResourceHandler);
|
||||||
|
///< It will be automatically deleted at the end of the program.
|
||||||
|
virtual ~cCiResourceHandler();
|
||||||
|
virtual const uint32_t *ResourceIds(void) const = 0;
|
||||||
|
///< Returns a pointer to an array of resource identifiers, where the
|
||||||
|
///< last value is zero.
|
||||||
|
virtual cCiSession *GetNewCiSession(uint32_t ResourceId, uint16_t SessionId, cCiTransportConnection *Tc) = 0;
|
||||||
|
///< Returns a new cCiSession, according to the given ResourceId.
|
||||||
|
};
|
||||||
|
|
||||||
|
class cCiResourceHandlers : public cList<cCiResourceHandler> {
|
||||||
|
private:
|
||||||
|
cVector<uint32_t> resourceIds;
|
||||||
|
public:
|
||||||
|
cCiResourceHandlers(void);
|
||||||
|
///< Creates the default list of resourceIds.
|
||||||
|
void Register(cCiResourceHandler *ResourceHandler);
|
||||||
|
///< Adds the given ResourceHandler to the list of resource handlers and
|
||||||
|
///< appends its ResourceIds to the global resourceIds.
|
||||||
|
///< A plugin that implements additional CAM capabilities must call
|
||||||
|
///< this function to register its resources.
|
||||||
|
const uint32_t *Ids(void) { return &resourceIds[0]; }
|
||||||
|
int NumIds(void) { return resourceIds.Size(); }
|
||||||
|
cCiSession *GetNewCiSession(uint32_t ResourceId, uint16_t SessionId, cCiTransportConnection *Tc);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern cCiResourceHandlers CiResourceHandlers;
|
||||||
|
|
||||||
class cCiMMI;
|
class cCiMMI;
|
||||||
|
|
||||||
class cCiMenu {
|
class cCiMenu {
|
||||||
@ -73,7 +166,6 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
class cDevice;
|
class cDevice;
|
||||||
class cCamSlot;
|
|
||||||
|
|
||||||
enum eModuleStatus { msNone, msReset, msPresent, msReady };
|
enum eModuleStatus { msNone, msReset, msPresent, msReady };
|
||||||
|
|
||||||
@ -377,6 +469,12 @@ public:
|
|||||||
///< 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.
|
||||||
|
virtual bool TsPostProcess(uchar *Data);
|
||||||
|
///< If there is a cCiSession that needs to do additional processing on TS packets
|
||||||
|
///< (after the CAM has done the decryption), this function will call its
|
||||||
|
///< TsPostProcess() function to have it do whatever operations are necessary on
|
||||||
|
///< the given TsPacket.
|
||||||
|
///< Returns true if the TsPacket was in any way modified.
|
||||||
virtual bool Inject(uchar *Data, int Count);
|
virtual bool Inject(uchar *Data, int Count);
|
||||||
///< Sends all Count bytes of the given Data to the CAM, and returns true
|
///< Sends all Count bytes of the given Data to the CAM, and returns true
|
||||||
///< if this was possible. If the data can't be sent to the CAM completely,
|
///< if this was possible. If the data can't be sent to the CAM completely,
|
||||||
|
7
device.c
7
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.19 2017/05/09 11:24:47 kls Exp $
|
* $Id: device.c 4.20 2017/05/18 09:05:46 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
@ -1665,6 +1665,9 @@ void cDevice::Action(void)
|
|||||||
if (b) {
|
if (b) {
|
||||||
// Distribute the packet to all attached receivers:
|
// Distribute the packet to all attached receivers:
|
||||||
Lock();
|
Lock();
|
||||||
|
cCamSlot *cs = CamSlot();
|
||||||
|
if (cs)
|
||||||
|
cs->TsPostProcess(b);
|
||||||
int Pid = TsPid(b);
|
int Pid = TsPid(b);
|
||||||
bool IsScrambled = TsIsScrambled(b);
|
bool IsScrambled = TsIsScrambled(b);
|
||||||
for (int i = 0; i < MAXRECEIVERS; i++) {
|
for (int i = 0; i < MAXRECEIVERS; i++) {
|
||||||
@ -1673,7 +1676,7 @@ void cDevice::Action(void)
|
|||||||
Receiver->Receive(b, TS_SIZE);
|
Receiver->Receive(b, TS_SIZE);
|
||||||
// Check whether the TS packet is scrambled:
|
// Check whether the TS packet is scrambled:
|
||||||
if (Receiver->startScrambleDetection) {
|
if (Receiver->startScrambleDetection) {
|
||||||
if (cCamSlot *cs = CamSlot()) {
|
if (cs) {
|
||||||
int CamSlotNumber = cs->MasterSlotNumber();
|
int CamSlotNumber = cs->MasterSlotNumber();
|
||||||
if (Receiver->lastScrambledPacket < Receiver->startScrambleDetection)
|
if (Receiver->lastScrambledPacket < Receiver->startScrambleDetection)
|
||||||
Receiver->lastScrambledPacket = Receiver->startScrambleDetection;
|
Receiver->lastScrambledPacket = Receiver->startScrambleDetection;
|
||||||
|
Loading…
Reference in New Issue
Block a user