From 66ab78a40f5b57e20142a33484e32c785a0c4017 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sun, 7 Jan 2007 18:00:00 +0100 Subject: Version 1.5.0 - The CAM handling has been refactored. Instead of a cCiHandler per device there is now an abstract cCiAdapter and a cCamSlot. This allows each slot to be accessed individually. - The general 15 seconds workaround time before opening the CAM menu has been removed. If the CAM menu doesn't open within a timeout, the enter menu command is now sent again. - If a CAM is reset or pulled and reinserted, it now automatically starts decrypting the current channel again. - The Setup/CAM menu now dynamically refreshes its items and displays whether a CAM is present or ready. The 'Reset' function no longer leaves the menu. - The CAM menu will now be openend when pressing the Ok key on a slot entry. - The CAM menu now stays within the current menu context and doesn't close and reopen the menu every time an option is selected. - When an encrypted channel is switched to for the first time, VDR now checks explicitly whether a CAM can actually decrypt that channel. If there is more than one CAM in the system that claims to be able to decrypt the channel, they are all tried in turn. To make this possible, an encrypted channel needs to be received in Transfer Mode when it is switched to for the first time, so that VDR can determine whether the TS packets are actually decrypted. Once a channel is known to be decrypted by a particular CAM, the next time it is switched to it will be shown in normal live viewing mode. - A cDevice now automatically detaches all cReceiver objects that receive PIDs that can't be decrypted with the current CAM. A plugin that attaches a cReceiver to a device should therefore watch the receiver's IsAttached() function to see if it is still attached to the device. - The cReceiver constructor no longer takes an 'int Ca' as its first parameter, but rather a 'tChannelID ChannelID'. This is necessary for the device to be able to determine which CAM a particular channel can be decrypted with. If the channel is known to be unencrypted, or a plugin doesn't want to provide the channel id for other reasons, an invalid tChannelID() can be given. - The cThread::Start() function now waits until a previous incarnation of this thread has actually stopped. Before this it could happen that a thread's Cancel(-1) function was called and immediately after that it was started again, but the Start() function still found it to be 'active'. - The parameter NeedsDetachReceivers in cDevice::GetDevice(const cChannel *Channel, ...) has been removed. A call to this function will automatically detach all receivers from the device if it returns a non-NULL pointer. - The cTimeMs class now accepts an initial timeout value in its constructor. - A CAM is now explicitly instructed to stop decrypting when switching away from an encrypted channel. - If the CAM in use can decrypt several channels at the same time, VDR can now make use if this capability. Whether or not a CAM can decrypt more than one channel is determined by sending it an initial empty QUERY command and testing whether it replies to it. - Ca values in the range 0...F in channels.conf can still be used to assign a channel to a particular device, but this will no longer work with encrypted channels because without valid CA ids VDR can't decide which CAM slot to use. However, since VDR now automatically determines which CAM can decrypt which channel, setting fixed channel/device relations should no longer be necessary. IF AN ENCRYPTED CHANNEL CAN'T BE DECRYPTED AND YOU HAVE A CA VALUE IN THE RANGE 0...F FOR THAT CHANNEL, SET IT TO 0 (FTA) AND TUNE TO THE CHANNEL AGAIN. --- ci.h | 247 ++++++++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 164 insertions(+), 83 deletions(-) (limited to 'ci.h') diff --git a/ci.h b/ci.h index 4574010..37514a5 100644 --- a/ci.h +++ b/ci.h @@ -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/03 12:49:10 kls Exp $ */ #ifndef __CI_H @@ -12,13 +12,18 @@ #include #include +#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 { -public: - bool active; - int pid; - int streamType; - cCiCaPidData(int Pid, int StreamType) - { - active = false; - pid = Pid; - streamType = StreamType; - } - }; +enum eModuleStatus { msNone, msReset, msPresent, msReady }; -class cCiCaProgramData : public cListObject { +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: - int programNumber; - cList pidList; - cCiCaProgramData(int ProgramNumber) - { - programNumber = ProgramNumber; - } + 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 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 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 {}; + +extern cCamSlots CamSlots; + +class cChannelCamRelation; + +class cChannelCamRelations : public cList { +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 -- cgit v1.2.3