1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Improved CAM support

This commit is contained in:
Klaus Schmidinger 2003-02-09 11:54:22 +01:00
parent b7777e230c
commit 777f330c77
8 changed files with 224 additions and 174 deletions

View File

@ -446,6 +446,7 @@ Oliver Endriss <o.endriss@gmx.de>
Reinhard Walter Buchner <rw.buchner@freenet.de>
for adding some satellites to 'sources.conf'
for his help in testing tuning with "Motor-DiSEqC"
for his help in debugging CAM support
Lauri Tischler <lauri.tischler@efore.fi>
for helping to test and debug the new channel source and DiSEqC handling

View File

@ -1947,3 +1947,9 @@ Video Disk Recorder Revision History
- Fixed a new/delete malloc/free mismatch in ringbuffer.c (thanks to Stefan
Huelswitt for reporting this one).
- Improved CAM handling.
2003-02-09: Version 1.1.24
- Improved CAM handling (thanks to Reinhard Walter Buchner for a great deal of help
in debugging this). It is now possible to insert the CAM in any of the two slots,
to insert and remove it while VDR is running and even to have two CAMs inserted.

221
ci.c
View File

@ -4,15 +4,11 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: ci.c 1.3 2003/02/02 15:49:52 kls Exp $
* $Id: ci.c 1.4 2003/02/09 11:54:22 kls Exp $
*/
/* XXX TODO
- handle slots separately
- use return values
- update CA descriptors in case they change
- dynamically react on CAM insert/remove
- implement CAM reset (per slot)
XXX*/
#include "ci.h"
@ -215,7 +211,7 @@ int cTPDU::Write(int fd)
int cTPDU::Read(int fd)
{
size = read(fd, data, sizeof(data));
size = safe_read(fd, data, sizeof(data));
if (size < 0) {
esyslog("ERROR: %m");
size = 0;
@ -229,15 +225,15 @@ void cTPDU::Dump(bool Outgoing)
{
if (DumpTPDUDataTransfer) {
#define MAX_DUMP 256
printf("%s ", Outgoing ? "-->" : "<--");
fprintf(stderr, "%s ", Outgoing ? "-->" : "<--");
for (int i = 0; i < size && i < MAX_DUMP; i++)
printf("%02X ", data[i]);
printf("%s\n", size >= MAX_DUMP ? "..." : "");
fprintf(stderr, "%02X ", data[i]);
fprintf(stderr, "%s\n", size >= MAX_DUMP ? "..." : "");
if (!Outgoing) {
printf(" ");
fprintf(stderr, " ");
for (int i = 0; i < size && i < MAX_DUMP; i++)
printf("%2c ", isprint(data[i]) ? data[i] : '.');
printf("%s\n", size >= MAX_DUMP ? "..." : "");
fprintf(stderr, "%2c ", isprint(data[i]) ? data[i] : '.');
fprintf(stderr, "%s\n", size >= MAX_DUMP ? "..." : "");
}
}
}
@ -288,6 +284,7 @@ private:
public:
cCiTransportConnection(void);
~cCiTransportConnection();
int Slot(void) const { return slot; }
int SendData(int Length, const uint8_t *Data);
int RecvData(void);
const uint8_t *Data(int &Length);
@ -324,15 +321,15 @@ int cCiTransportConnection::SendTPDU(uint8_t Tag, int Length, const uint8_t *Dat
return TPDU.Write(fd);
}
#define CAM_READ_TIMEOUT 3500 // ms
int cCiTransportConnection::RecvTPDU(void)
{
//XXX poll, timeout???
struct pollfd pfd[1];
pfd[0].fd = fd;
pfd[0].events = POLLIN;
lastResponse = ERROR;
if (poll(pfd, 1, 3500/*XXX*/) && (pfd[0].revents & POLLIN))//XXX
if (tpdu->Read(fd) == OK && tpdu->Tcid() == tcid) {
if (poll(pfd, 1, CAM_READ_TIMEOUT) && (pfd[0].revents & POLLIN) && tpdu->Read(fd) == OK && tpdu->Tcid() == tcid) {
switch (state) {
case stIDLE: break;
case stCREATION: if (tpdu->Tag() == T_CTC_REPLY) {
@ -363,6 +360,10 @@ int cCiTransportConnection::RecvTPDU(void)
break;
}
}
else {
esyslog("ERROR: CAM: Read failed: slot %d, tcid %d\n", slot, tcid);
Init(-1, slot, tcid);
}
return lastResponse;
}
@ -385,11 +386,8 @@ int cCiTransportConnection::SendData(int Length, const uint8_t *Data)
int cCiTransportConnection::RecvData(void)
{
if (SendTPDU(T_RCV) == OK) {
if (RecvTPDU() == OK) {
//XXX
}
}
if (SendTPDU(T_RCV) == OK)
return RecvTPDU();
return ERROR;
}
@ -403,7 +401,18 @@ int cCiTransportConnection::CreateConnection(void)
if (state == stIDLE) {
if (SendTPDU(T_CREATE_TC) == OK) {
state = stCREATION;
if (RecvTPDU() == T_CTC_REPLY)
return OK;
// the following is a workaround for CAMs that don't quite follow the specs...
else {
dbgprotocol("*** no reaction on T_CREATE_TC - retrying\n");
if (RecvTPDU() == T_CTC_REPLY) {
dbgprotocol("*** received T_CTC_REPLY\n");
RecvTPDU();
dbgprotocol("*** done dummy RecvTPDU()\n");
}
return OK;
}
}
}
return ERROR;
@ -412,10 +421,9 @@ int cCiTransportConnection::CreateConnection(void)
int cCiTransportConnection::Poll(void)
{
if (state == stACTIVE) {
if (SendTPDU(T_DATA_LAST) == OK) {
if (SendTPDU(T_DATA_LAST) == OK)
return RecvTPDU();
}
}
return ERROR;
}
@ -428,11 +436,12 @@ private:
int fd;
int numSlots;
cCiTransportConnection tc[MAX_CI_CONNECT];
bool ResetSlot(int Slot);
public:
cCiTransportLayer(int Fd, int NumSlots);
cCiTransportConnection *NewConnection(void);
int Process(void);
cCiTransportConnection *NewConnection(int Slot);
bool ResetSlot(int Slot);
bool ModuleReady(int Slot);
cCiTransportConnection *Process(int Slot);
};
cCiTransportLayer::cCiTransportLayer(int Fd, int NumSlots)
@ -441,58 +450,54 @@ cCiTransportLayer::cCiTransportLayer(int Fd, int NumSlots)
numSlots = NumSlots;
for (int s = 0; s < numSlots; s++)
ResetSlot(s);
for (int i = 0; i < MAX_CI_CONNECT; i++)
tc[i].Init(fd, 0/*XXX*/, i + 1);
}
cCiTransportConnection *cCiTransportLayer::NewConnection(void)
cCiTransportConnection *cCiTransportLayer::NewConnection(int Slot)
{
for (int i = 0; i < MAX_CI_CONNECT; i++) {
if (tc[i].State() == stIDLE) {
if (tc[i].CreateConnection() == OK) {
if (tc[i].RecvTPDU() == T_CTC_REPLY)
dbgprotocol("Creating connection: slot %d, tcid %d\n", Slot, i + 1);
tc[i].Init(fd, Slot, i + 1);
if (tc[i].CreateConnection() == OK)
return &tc[i];
}
break;
}
}
return NULL;
}
#define CA_RESET_TIMEOUT 3 // seconds
bool cCiTransportLayer::ResetSlot(int Slot)
{
dbgprotocol("Resetting slot %d...", Slot);
ca_slot_info_t sinfo;
sinfo.num = Slot;
if (ioctl(fd, CA_RESET, 1 << Slot) != -1) {
time_t t0 = time(NULL);
do {
if (ioctl(fd, CA_GET_SLOT_INFO, &sinfo) != -1) {
ioctl(fd, CA_GET_SLOT_INFO, &sinfo);
if ((sinfo.flags & CA_CI_MODULE_READY) != 0) {
dbgprotocol("ok.\n");
return true;
}
}
else {
esyslog("ERROR: can't get info on CAM slot %d: %m", Slot);
break;
}
} while (time(NULL) - t0 < CA_RESET_TIMEOUT);
}
else
esyslog("ERROR: can't reset CAM slot %d: %m", Slot);
dbgprotocol("failed!\n");
return false;
}
int cCiTransportLayer::Process(void)
bool cCiTransportLayer::ModuleReady(int Slot)
{
ca_slot_info_t sinfo;
sinfo.num = Slot;
if (ioctl(fd, CA_GET_SLOT_INFO, &sinfo) != -1)
return sinfo.flags & CA_CI_MODULE_READY;
else
esyslog("ERROR: can't get info on CAM slot %d: %m", Slot);
return false;
}
cCiTransportConnection *cCiTransportLayer::Process(int Slot)
{
for (int i = 0; i < MAX_CI_CONNECT; i++) {
cCiTransportConnection *Tc = &tc[i];
if (Tc->State() == stACTIVE) {
if (Tc->Slot() == Slot) {
switch (Tc->State()) {
case stCREATION:
case stACTIVE:
if (!Tc->DataAvailable()) {
if (Tc->Poll() != OK)
;//XXX continue;
@ -512,11 +517,17 @@ int cCiTransportLayer::Process(void)
case ERROR:
default:
//XXX Tc->state = stIDLE;//XXX Init()???
return NULL;
break;
}
//XXX this will only work with _one_ transport connection per slot!
return Tc;
break;
default: ;
}
}
return OK;
}
return NULL;
}
// -- cCiSession -------------------------------------------------------------
@ -608,6 +619,7 @@ protected:
public:
cCiSession(int SessionId, int ResourceId, cCiTransportConnection *Tc);
virtual ~cCiSession();
const cCiTransportConnection *Tc(void) { return tc; }
int SessionId(void) { return sessionId; }
int ResourceId(void) { return resourceId; }
virtual bool Process(int Length = 0, const uint8_t *Data = NULL);
@ -1261,9 +1273,7 @@ cCiHandler::cCiHandler(int Fd, int NumSlots)
for (int i = 0; i < MAX_CI_SESSION; i++)
sessions[i] = NULL;
tpl = new cCiTransportLayer(Fd, numSlots);
tc = tpl->NewConnection();
if (!tc)
isyslog("CAM: no CAM detected");
tc = NULL;
}
cCiHandler::~cCiHandler()
@ -1281,14 +1291,9 @@ cCiHandler *cCiHandler::CreateCiHandler(const char *FileName)
if (ioctl(fd_ca, CA_GET_CAP, &Caps) == 0) {
int NumSlots = Caps.slot_num;
if (NumSlots > 0) {
dsyslog("CAM: found %d CAM slots", NumSlots);
if (Caps.slot_type == CA_CI_LINK) {
cCiHandler *CiHandler = new cCiHandler(fd_ca, NumSlots);
// drive the initial data exchange:
for (int i = 0; i < 20; i++) //XXX make this dynamic???
CiHandler->Process();
return CiHandler;
}
//XXX dsyslog("CAM: found %d CAM slots", NumSlots); // TODO let's do this only once we can be sure that there _really_ is a CAM adapter!
if (Caps.slot_type == CA_CI_LINK)
return new cCiHandler(fd_ca, NumSlots);
else
esyslog("ERROR: CAM doesn't support link layer interface");
}
@ -1320,7 +1325,7 @@ bool cCiHandler::Send(uint8_t Tag, int SessionId, int ResourceId, int Status)
*(short *)p = htons(SessionId);
p += 2;
buffer[1] = p - buffer - 2; // length
return tc->SendData(p - buffer, buffer) == OK;
return tc && tc->SendData(p - buffer, buffer) == OK;
}
cCiSession *cCiHandler::GetSessionBySessionId(int SessionId)
@ -1332,10 +1337,10 @@ cCiSession *cCiHandler::GetSessionBySessionId(int SessionId)
return NULL;
}
cCiSession *cCiHandler::GetSessionByResourceId(int ResourceId)
cCiSession *cCiHandler::GetSessionByResourceId(int ResourceId, int Slot)
{
for (int i = 0; i < MAX_CI_SESSION; i++) {
if (sessions[i] && sessions[i]->ResourceId() == ResourceId)
if (sessions[i] && sessions[i]->Tc()->Slot() == Slot && sessions[i]->ResourceId() == ResourceId)
return sessions[i];
}
return NULL;
@ -1343,7 +1348,7 @@ cCiSession *cCiHandler::GetSessionByResourceId(int ResourceId)
cCiSession *cCiHandler::CreateSession(int ResourceId)
{
if (!GetSessionByResourceId(ResourceId)) {
if (!GetSessionByResourceId(ResourceId, tc->Slot())) {
for (int i = 0; i < MAX_CI_SESSION; i++) {
if (!sessions[i]) {
switch (ResourceId) {
@ -1403,11 +1408,24 @@ bool cCiHandler::CloseSession(int SessionId)
return false;
}
bool cCiHandler::Process(void)
int cCiHandler::CloseAllSessions(int Slot)
{
int result = 0;
for (int i = 0; i < MAX_CI_SESSION; i++) {
if (sessions[i] && sessions[i]->Tc()->Slot() == Slot) {
CloseSession(sessions[i]->SessionId());
result++;
}
}
return result;
}
void cCiHandler::Process(void)
{
if (tc) {
cMutexLock MutexLock(&mutex);
if (tpl->Process() == OK) {
for (int Slot = 0; Slot < numSlots; Slot++) {
tc = tpl->Process(Slot);
if (tc) {
int Length;
const uint8_t *Data = tc->Data(Length);
if (Data && Length > 1) {
@ -1416,68 +1434,81 @@ bool cCiHandler::Process(void)
int SessionId = ntohs(*(short *)&Data[2]);
cCiSession *Session = GetSessionBySessionId(SessionId);
if (Session)
return Session->Process(Length - 4, Data + 4);
else {
Session->Process(Length - 4, Data + 4);
else
esyslog("ERROR: unknown session id: %d", SessionId);
return false;
}
}
break;
case ST_OPEN_SESSION_REQUEST: return OpenSession(Length, Data);
case ST_OPEN_SESSION_REQUEST: OpenSession(Length, Data);
break;
case ST_CLOSE_SESSION_REQUEST: if (Length == 4)
return CloseSession(ntohs(*(short *)&Data[2]));
CloseSession(ntohs(*(short *)&Data[2]));
break;
case ST_CREATE_SESSION_RESPONSE: //XXX fall through to default
case ST_CLOSE_SESSION_RESPONSE: //XXX fall through to default
default: esyslog("ERROR: unknown session tag: %02X", *Data);
return false;
}
return true;
}
}
else {
if (!CloseAllSessions(Slot)) {
if (tpl->ModuleReady(Slot)) {
dbgprotocol("Module ready in slot %d\n", Slot);
tpl->NewConnection(Slot);
}
}
}
}
for (int i = 0; i < MAX_CI_SESSION; i++) {
if (sessions[i])
sessions[i]->Process();//XXX retval???
sessions[i]->Process();
}
}
}
return false;
}
bool cCiHandler::EnterMenu(void)
bool cCiHandler::EnterMenu(int Slot)
{
cMutexLock MutexLock(&mutex);
//XXX slots???
cCiApplicationInformation *api = (cCiApplicationInformation *)GetSessionByResourceId(RI_APPLICATION_INFORMATION);
cCiApplicationInformation *api = (cCiApplicationInformation *)GetSessionByResourceId(RI_APPLICATION_INFORMATION, Slot);
return api ? api->EnterMenu() : false;
}
cCiMenu *cCiHandler::GetMenu(void)
{
cMutexLock MutexLock(&mutex);
//XXX slots???
cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI);
return mmi ? mmi->Menu() : NULL;
for (int Slot = 0; Slot < numSlots; Slot++) {
cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI, Slot);
if (mmi)
return mmi->Menu();
}
return NULL;
}
cCiEnquiry *cCiHandler::GetEnquiry(void)
{
cMutexLock MutexLock(&mutex);
//XXX slots???
cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI);
return mmi ? mmi->Enquiry() : NULL;
for (int Slot = 0; Slot < numSlots; Slot++) {
cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI, Slot);
if (mmi)
return mmi->Enquiry();
}
return NULL;
}
bool cCiHandler::SetCaPmt(cCiCaPmt &CaPmt)
{
cMutexLock MutexLock(&mutex);
//XXX slots???
cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
return cas ? cas->SendPMT(CaPmt) : false;
bool result = false;
for (int Slot = 0; Slot < numSlots; Slot++) {
cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT, Slot);
if (cas)
result |= cas->SendPMT(CaPmt);
}
return result;
}
bool cCiHandler::Reset(void)
bool cCiHandler::Reset(int Slot)
{
cMutexLock MutexLock(&mutex);
//XXX slots???
return false;//XXX not yet implemented
CloseAllSessions(Slot);
return tpl->ResetSlot(Slot);
}

11
ci.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: ci.h 1.1 2003/01/06 12:31:09 kls Exp $
* $Id: ci.h 1.2 2003/02/09 11:44:00 kls Exp $
*/
#ifndef __CI_H
@ -86,20 +86,21 @@ private:
int ResourceIdToInt(const uint8_t *Data);
bool Send(uint8_t Tag, int SessionId, int ResourceId = 0, int Status = -1);
cCiSession *GetSessionBySessionId(int SessionId);
cCiSession *GetSessionByResourceId(int ResourceId);
cCiSession *GetSessionByResourceId(int ResourceId, int Slot);
cCiSession *CreateSession(int ResourceId);
bool OpenSession(int Length, const uint8_t *Data);
bool CloseSession(int SessionId);
int CloseAllSessions(int Slot);
cCiHandler(int Fd, int NumSlots);
public:
~cCiHandler();
static cCiHandler *CreateCiHandler(const char *FileName);
bool Process(void);
bool EnterMenu(void);
void Process(void);
bool EnterMenu(int Slot);
cCiMenu *GetMenu(void);
cCiEnquiry *GetEnquiry(void);
bool SetCaPmt(cCiCaPmt &CaPmt);
bool Reset(void);
bool Reset(int Slot);
};
#endif //__CI_H

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: config.h 1.147 2003/01/26 19:50:19 kls Exp $
* $Id: config.h 1.148 2003/02/08 10:25:44 kls Exp $
*/
#ifndef __CONFIG_H
@ -19,7 +19,7 @@
#include "device.h"
#include "tools.h"
#define VDRVERSION "1.1.23"
#define VDRVERSION "1.1.24"
#define MAXPRIORITY 99
#define MAXLIFETIME 99

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbdevice.c 1.42 2003/02/02 15:31:31 kls Exp $
* $Id: dvbdevice.c 1.43 2003/02/09 11:47:02 kls Exp $
*/
#include "dvbdevice.h"
@ -238,6 +238,7 @@ bool cDvbTuner::SetFrontend(void)
void cDvbTuner::Action(void)
{
time_t StartTime = time(NULL);
dsyslog("tuner thread started on device %d (pid=%d)", cardIndex + 1, getpid());
active = true;
while (active) {
@ -258,7 +259,9 @@ void cDvbTuner::Action(void)
continue;
}
}
if (ciHandler && !caSet) {//XXX TODO update in case the CA descriptors have changed
if (ciHandler) {
ciHandler->Process();
if (!caSet) {//XXX TODO update in case the CA descriptors have changed
uchar buffer[2048];
int length = cSIProcessor::GetCaDescriptors(channel.Source(), channel.Frequency(), channel.Sid(), sizeof(buffer), buffer);
if (length > 0) {
@ -275,7 +278,9 @@ void cDvbTuner::Action(void)
caSet = ciHandler->SetCaPmt(CaPmt);
}
}
newSet.TimedWait(mutex, 1000);
}
// in the beginning we loop more often to let the CAM connection start up fast
newSet.TimedWait(mutex, (ciHandler && (time(NULL) - StartTime < 20)) ? 100 : 1000);
}
dsyslog("tuner thread ended on device %d (pid=%d)", cardIndex + 1, getpid());
}

22
menu.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: menu.c 1.232 2003/01/19 14:59:46 kls Exp $
* $Id: menu.c 1.233 2003/02/09 10:46:25 kls Exp $
*/
#include "menu.h"
@ -1537,6 +1537,7 @@ cMenuCam::cMenuCam(cCiMenu *CiMenu)
Add(new cOsdItem(ciMenu->SubTitleText()));
Add(new cOsdItem(ciMenu->BottomText()));
Display();
dsyslog("CAM: Menu - %s", ciMenu->TitleText());
}
cMenuCam::~cMenuCam()
@ -1622,7 +1623,6 @@ cOsdObject *CamControl(void)
if (Device) {
cCiHandler *CiHandler = Device->CiHandler();
if (CiHandler) {
CiHandler->Process();
cCiMenu *CiMenu = CiHandler->GetMenu();
if (CiMenu)
return new cMenuCam(CiMenu);
@ -2069,7 +2069,7 @@ class cMenuSetupCICAM : public cMenuSetupBase {
private:
int helpKeys;
void SetHelpKeys(void);
cCiHandler *GetCurrentCiHandler(void);
cCiHandler *GetCurrentCiHandler(int *Slot = NULL);
eOSState Menu(void);
eOSState Reset(void);
public:
@ -2091,9 +2091,11 @@ cMenuSetupCICAM::cMenuSetupCICAM(void)
SetHelpKeys();
}
cCiHandler *cMenuSetupCICAM::GetCurrentCiHandler(void)
cCiHandler *cMenuSetupCICAM::GetCurrentCiHandler(int *Slot)
{
cDevice *Device = cDevice::GetDevice(Current() / 2);
if (Slot)
*Slot = Current() % 2;
return Device ? Device->CiHandler() : NULL;
}
@ -2112,8 +2114,9 @@ void cMenuSetupCICAM::SetHelpKeys(void)
eOSState cMenuSetupCICAM::Menu(void)
{
cCiHandler *CiHandler = GetCurrentCiHandler();
if (CiHandler && CiHandler->EnterMenu())
int Slot = 0;
cCiHandler *CiHandler = GetCurrentCiHandler(&Slot);
if (CiHandler && CiHandler->EnterMenu(Slot))
return osEnd; // the CAM menu will be executed explicitly from the main loop
else
Interface->Error(tr("Can't open CAM menu!"));
@ -2122,9 +2125,12 @@ eOSState cMenuSetupCICAM::Menu(void)
eOSState cMenuSetupCICAM::Reset(void)
{
cCiHandler *CiHandler = GetCurrentCiHandler();
if (CiHandler && CiHandler->Reset())
int Slot = 0;
cCiHandler *CiHandler = GetCurrentCiHandler(&Slot);
if (CiHandler && CiHandler->Reset(Slot)) {
Interface->Info(tr("CAM has been reset"));
return osEnd;
}
else
Interface->Error(tr("Can't reset CAM!"));
return osContinue;

4
vdr.c
View File

@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/people/kls/vdr
*
* $Id: vdr.c 1.141 2003/01/26 11:56:31 kls Exp $
* $Id: vdr.c 1.142 2003/02/09 11:25:38 kls Exp $
*/
#include <getopt.h>
@ -469,7 +469,7 @@ int main(int argc, char *argv[])
}
}
// CAM control:
if (!Menu)
if (!Interface->IsOpen())
Menu = CamControl();
// User Input:
cOsdObject *Interact = Menu ? Menu : cControl::Control();