summaryrefslogtreecommitdiff
path: root/ci.c
diff options
context:
space:
mode:
Diffstat (limited to 'ci.c')
-rw-r--r--ci.c311
1 files changed, 171 insertions, 140 deletions
diff --git a/ci.c b/ci.c
index 97de9b01..f99be7d4 100644
--- a/ci.c
+++ b/ci.c
@@ -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;
- return OK;
+ 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,9 +421,8 @@ 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,46 +450,28 @@ 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)
- return &tc[i];
- }
+ 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);
+ dbgprotocol("ok.\n");
+ return true;
}
else
esyslog("ERROR: can't reset CAM slot %d: %m", Slot);
@@ -488,35 +479,55 @@ bool cCiTransportLayer::ResetSlot(int Slot)
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->DataAvailable()) {
- if (Tc->Poll() != OK)
- ;//XXX continue;
- }
- switch (Tc->LastResponse()) {
- case T_REQUEST_TC:
- //XXX
- break;
- case T_DATA_MORE:
- case T_DATA_LAST:
- case T_CTC_REPLY:
- case T_SB:
- if (Tc->DataAvailable())
- Tc->RecvData();
- break;
- case TIMEOUT:
- case ERROR:
- default:
- //XXX Tc->state = stIDLE;//XXX Init()???
+ if (Tc->Slot() == Slot) {
+ switch (Tc->State()) {
+ case stCREATION:
+ case stACTIVE:
+ if (!Tc->DataAvailable()) {
+ if (Tc->Poll() != OK)
+ ;//XXX continue;
+ }
+ switch (Tc->LastResponse()) {
+ case T_REQUEST_TC:
+ //XXX
+ break;
+ case T_DATA_MORE:
+ case T_DATA_LAST:
+ case T_CTC_REPLY:
+ case T_SB:
+ if (Tc->DataAvailable())
+ Tc->RecvData();
+ break;
+ case TIMEOUT:
+ 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,81 +1408,107 @@ bool cCiHandler::CloseSession(int SessionId)
return false;
}
-bool cCiHandler::Process(void)
-{
- if (tc) {
- cMutexLock MutexLock(&mutex);
- if (tpl->Process() == OK) {
- int Length;
- const uint8_t *Data = tc->Data(Length);
- if (Data && Length > 1) {
- switch (*Data) {
- case ST_SESSION_NUMBER: if (Length > 4) {
- int SessionId = ntohs(*(short *)&Data[2]);
- cCiSession *Session = GetSessionBySessionId(SessionId);
- if (Session)
- return 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_CLOSE_SESSION_REQUEST: if (Length == 4)
- return 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;
- }
- for (int i = 0; i < MAX_CI_SESSION; i++) {
- if (sessions[i])
- sessions[i]->Process();//XXX retval???
+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)
+{
+ cMutexLock MutexLock(&mutex);
+ 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) {
+ switch (*Data) {
+ case ST_SESSION_NUMBER: if (Length > 4) {
+ int SessionId = ntohs(*(short *)&Data[2]);
+ cCiSession *Session = GetSessionBySessionId(SessionId);
+ if (Session)
+ Session->Process(Length - 4, Data + 4);
+ else
+ esyslog("ERROR: unknown session id: %d", SessionId);
+ }
+ break;
+ case ST_OPEN_SESSION_REQUEST: OpenSession(Length, Data);
+ break;
+ case ST_CLOSE_SESSION_REQUEST: if (Length == 4)
+ 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;
+ }
+ 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();
+ }
}
-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);
}