CAM handling refactored; multiple recordings with one CAM; automatic CAM selection

This commit is contained in:
Klaus Schmidinger
2007-01-07 14:46:14 +01:00
parent b4cab10eca
commit 87dd5139ff
32 changed files with 2116 additions and 1457 deletions

249
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.22 2006/08/12 09:43:31 kls Exp $
* $Id: ci.h 1.23 2007/01/07 14:38:00 kls Exp $
*/
#ifndef __CI_H
@@ -12,13 +12,18 @@
#include <stdint.h>
#include <stdio.h>
#include "channels.h"
#include "thread.h"
#include "tools.h"
#define MAX_CAM_SLOTS_PER_ADAPTER 8 // maximum possible value is 255
#define MAX_CONNECTIONS_PER_CAM_SLOT 8 // maximum possible value is 254
#define CAM_READ_TIMEOUT 50 // ms
class cCiMMI;
class cCiMenu {
friend class cCiHandler;
friend class cCamSlot;
friend class cCiMMI;
private:
enum { MAX_CIMENU_ENTRIES = 64 }; ///< XXX is there a specified maximum?
@@ -40,14 +45,14 @@ public:
const char *Entry(int n) { return n < numEntries ? entries[n] : NULL; }
int NumEntries(void) { return numEntries; }
bool Selectable(void) { return selectable; }
bool Select(int Index);
bool Cancel(void);
bool Abort(void);
void Select(int Index);
void Cancel(void);
void Abort(void);
bool HasUpdate(void);
};
class cCiEnquiry {
friend class cCiHandler;
friend class cCamSlot;
friend class cCiMMI;
private:
cCiMMI *mmi;
@@ -61,103 +66,140 @@ public:
const char *Text(void) { return text; }
bool Blind(void) { return blind; }
int ExpectedLength(void) { return expectedLength; }
bool Reply(const char *s);
bool Cancel(void);
bool Abort(void);
void Reply(const char *s);
void Cancel(void);
void Abort(void);
};
#define MAX_CI_SESSION 16 //XXX
#define MAX_CI_SLOT 16
class cDevice;
class cCamSlot;
class cCiCaPidData : public cListObject {
enum eModuleStatus { msNone, msReset, msPresent, msReady };
class cCiAdapter : public cThread {
friend class cCamSlot;
private:
cDevice *assignedDevice;
cCamSlot *camSlots[MAX_CAM_SLOTS_PER_ADAPTER];
void AddCamSlot(cCamSlot *CamSlot);
///< Adds the given CamSlot to this CI adapter.
protected:
virtual void Action(void);
///< Handles the attached CAM slots in a separate thread.
///< The derived class must call the Start() function to
///< actually start CAM handling.
virtual int Read(uint8_t *Buffer, int MaxLength) = 0;
///< Reads one chunk of data into the given Buffer, up to MaxLength bytes.
///< If no data is available immediately, wait for up to CAM_READ_TIMEOUT.
///< Returns the number of bytes read (in case of an error it will also
///< return 0).
virtual void Write(const uint8_t *Buffer, int Length) = 0;
///< Writes Length bytes of the given Buffer.
virtual bool Reset(int Slot) = 0;
///< Resets the CAM in the given Slot.
///< Returns true if the operation was successful.
virtual eModuleStatus ModuleStatus(int Slot) = 0;
///< Returns the status of the CAM in the given Slot.
virtual bool Assign(cDevice *Device, bool Query = false) = 0;
///< Assigns this adapter to the given Device, if this is possible.
///< If Query is 'true', the adapter only checks whether it can be
///< assigned to the Device, but doesn't actually assign itself to it.
///< Returns true if the adapter can be assigned to the Device.
///< If Device is NULL, the adapter will be unassigned from any
///< device it was previously assigned to. The value of Query
///< is ignored in that case, and this function always returns
///< 'true'.
public:
bool active;
int pid;
int streamType;
cCiCaPidData(int Pid, int StreamType)
{
active = false;
pid = Pid;
streamType = StreamType;
}
cCiAdapter(void);
virtual ~cCiAdapter();
///< The derived class must call Cancel(3) in its destructor.
virtual bool Ready(void);
///< Returns 'true' if all present CAMs in this adapter are ready.
};
class cCiCaProgramData : public cListObject {
public:
int programNumber;
cList<cCiCaPidData> pidList;
cCiCaProgramData(int ProgramNumber)
{
programNumber = ProgramNumber;
}
};
class cCiSession;
class cCiTransportLayer;
class cTPDU;
class cCiTransportConnection;
class cCiSession;
class cCiCaProgramData;
class cCiHandler {
class cCamSlot : public cListObject {
friend class cCiAdapter;
friend class cCiTransportConnection;
private:
cMutex mutex;
int fd;
int numSlots;
bool newCaSupport;
bool hasUserIO;
bool moduleReady[MAX_CI_SLOT];
cCiSession *sessions[MAX_CI_SESSION];
cCiTransportLayer *tpl;
cCiTransportConnection *tc;
cCondVar processed;
cCiAdapter *ciAdapter;
int slotIndex;
int slotNumber;
cCiTransportConnection *tc[MAX_CONNECTIONS_PER_CAM_SLOT + 1]; // connection numbering starts with 1
eModuleStatus lastModuleStatus;
time_t resetTime;
cTimeMs moduleCheckTimer;
bool resendPmt;
int source;
int transponder;
cList<cCiCaProgramData> caProgramList;
uint32_t ResourceIdToInt(const uint8_t *Data);
bool Send(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId = 0, int Status = -1);
const unsigned short *GetCaSystemIds(int Slot);
cCiSession *GetSessionBySessionId(uint16_t SessionId);
cCiSession *GetSessionByResourceId(uint32_t ResourceId, int Slot);
cCiSession *CreateSession(uint32_t ResourceId);
bool OpenSession(int Length, const uint8_t *Data);
bool CloseSession(uint16_t SessionId);
int CloseAllSessions(int Slot);
cCiHandler(int Fd, int NumSlots);
void SendCaPmt(void);
const int *GetCaSystemIds(void);
void SendCaPmt(uint8_t CmdId);
void NewConnection(void);
void DeleteAllConnections(void);
void Process(cTPDU *TPDU = NULL);
void Write(cTPDU *TPDU);
cCiSession *GetSessionByResourceId(uint32_t ResourceId);
public:
~cCiHandler();
static cCiHandler *CreateCiHandler(const char *FileName);
///< Creates a new cCiHandler for the given CA device.
int NumSlots(void) { return numSlots; }
///< Returns the number of CAM slots provided by this CA device.
int NumCams(void);
///< Returns the number of actual CAMs inserted into this CA device.
cCamSlot(cCiAdapter *CiAdapter);
///< Creates a new CAM slot for the given CiAdapter.
///< The CiAdapter will take care of deleting the CAM slot,
///< so the caller must not delete it!
virtual ~cCamSlot();
bool Assign(cDevice *Device, bool Query = false);
///< 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
///< it can be assigned to the Device, but doesn't actually assign itself to it.
///< Returns true if this slot can be assigned to the Device.
///< If Device is NULL, the slot will be unassigned from any
///< device it was previously assigned to. The value of Query
///< is ignored in that case, and this function always returns
///< 'true'.
cDevice *Device(void);
///< Returns the device this CAM slot is currently assigned to.
int SlotIndex(void) { return slotIndex; }
///< Returns the index of this CAM slot within its CI adapter.
///< The first slot has an index of 0.
int SlotNumber(void) { return slotNumber; }
///< Returns the number of this CAM slot within the whole system.
///< The first slot has the number 1.
bool Reset(void);
///< Resets the CAM in this slot.
///< Returns true if the operation was successful.
eModuleStatus ModuleStatus(void);
///< Returns the status of the CAM in this slot.
const char *GetCamName(void);
///< Returns the name of the CAM in this slot, or NULL if there is
///< no ready CAM in this slot.
bool Ready(void);
///< Returns true if all CAMs in this CA device are ready.
bool Process(int Slot = -1);
///< Processes the given Slot. If Slot is -1, all slots are processed.
///< Returns false in case of an error.
bool HasUserIO(void) { return hasUserIO; }
///< Returns 'true' if the CAM in this slot is ready to decrypt.
bool HasMMI(void);
///< Returns 'true' if the CAM in this slot has an active MMI.
bool HasUserIO(void);
///< Returns true if there is a pending user interaction, which shall
///< be retrieved via GetMenu() or GetEnquiry().
bool EnterMenu(int Slot);
///< Requests the CAM in the given Slot to start its menu.
bool EnterMenu(void);
///< Requests the CAM in this slot to start its menu.
cCiMenu *GetMenu(void);
///< Gets a pending menu, or NULL if there is no menu.
cCiEnquiry *GetEnquiry(void);
///< Gets a pending enquiry, or NULL if there is no enquiry.
const char *GetCamName(int Slot);
///< Returns the name of the CAM in the given Slot, or NULL if there
///< is no CAM in that slot.
bool ProvidesCa(const unsigned short *CaSystemIds); //XXX Slot???
///< Returns true if any of the CAMs can provide one of the given
int Priority(void);
///< Returns the priority if the device this slot is currently assigned
///< to, or -1 if it is not assigned to any device.
bool ProvidesCa(const int *CaSystemIds);
///< Returns true if the CAM in this slot provides one of the given
///< CaSystemIds. This doesn't necessarily mean that it will be
///< possible to actually decrypt such a programme, since CAMs
///< usually advertise several CA system ids, while the actual
///< decryption is controlled by the smart card inserted into
///< the CAM.
void SetSource(int Source, int Transponder);
///< Sets the Source and Transponder of the device this cCiHandler is
///< currently tuned to. If Source or Transponder are different than
///< what was given in a previous call to SetSource(), any previously
///< added PIDs will be cleared.
void AddPid(int ProgramNumber, int Pid, int StreamType);
///< Adds the given PID information to the list of PIDs. A later call
///< to SetPid() will (de)activate one of these entries.
@@ -165,16 +207,55 @@ public:
///< Sets the given Pid (which has previously been added through a
///< call to AddPid()) to Active. A later call to StartDecrypting() will
///< send the full list of currently active CA_PMT entries to the CAM.
bool CanDecrypt(int ProgramNumber);
///< XXX
///< Returns true if there is a CAM in this CA device that is able
///< to decrypt the programme with the given ProgramNumber. The PIDs
///< for this ProgramNumber must have been set through previous calls
///< to SetPid().
void AddChannel(const cChannel *Channel);
///< Adds all PIDs if the given Channel to the current list of PIDs.
///< If the source or transponder of the channel are different than
///< what was given in a previous call to AddChannel(), any previously
///< added PIDs will be cleared.
bool CanDecrypt(const cChannel *Channel);
///< Returns true if there is a CAM in this slot that is able to decrypt
///< the given Channel (or at least claims to be able to do so).
///< Since the QUERY/REPLY mechanism for CAMs is pretty unreliable (some
///< CAMs don't reply to queries at all), we always return true if the
///< CAM is currently not decrypting anything. If there is already a
///< channel being decrypted, a call to CanDecrypt() checks whether the
///< CAM can also decrypt the given channel. Only CAMs that have replied
///< to the inital QUERY will perform this check at all. CAMs that never
///< replied to the initial QUERY are assumed not to be able to handle
///< more than one channel at a time.
void StartDecrypting(void);
///< Triggers sending all currently active CA_PMT entries to the CAM,
///< so that it will start decrypting.
bool Reset(int Slot);
void StopDecrypting(void);
///< Clears the list of CA_PMT entries and tells the CAM to stop decrypting.
bool IsDecrypting(void);
///< Returns true if the CAM in this slot is currently used for decrypting.
};
class cCamSlots : public cList<cCamSlot> {};
extern cCamSlots CamSlots;
class cChannelCamRelation;
class cChannelCamRelations : public cList<cChannelCamRelation> {
private:
cMutex mutex;
cChannelCamRelation *GetEntry(tChannelID ChannelID);
cChannelCamRelation *AddEntry(tChannelID ChannelID);
time_t lastCleanup;
void Cleanup(void);
public:
cChannelCamRelations(void);
void Reset(int CamSlotNumber);
bool CamChecked(tChannelID ChannelID, int CamSlotNumber);
bool CamDecrypt(tChannelID ChannelID, int CamSlotNumber);
void SetChecked(tChannelID ChannelID, int CamSlotNumber);
void SetDecrypt(tChannelID ChannelID, int CamSlotNumber);
void ClrChecked(tChannelID ChannelID, int CamSlotNumber);
void ClrDecrypt(tChannelID ChannelID, int CamSlotNumber);
};
extern cChannelCamRelations ChannelCamRelations;
#endif //__CI_H