vdr  1.7.27
ci.c
Go to the documentation of this file.
00001 /*
00002  * ci.c: Common Interface
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: ci.c 2.8 2012/02/29 10:24:41 kls Exp $
00008  */
00009 
00010 #include "ci.h"
00011 #include <ctype.h>
00012 #include <linux/dvb/ca.h>
00013 #include <malloc.h>
00014 #include <netinet/in.h>
00015 #include <poll.h>
00016 #include <string.h>
00017 #include <sys/ioctl.h>
00018 #include <time.h>
00019 #include <unistd.h>
00020 #include "device.h"
00021 #include "pat.h"
00022 #include "tools.h"
00023 
00024 // Set these to 'true' for debug output:
00025 static bool DumpTPDUDataTransfer = false;
00026 static bool DebugProtocol = false;
00027 static bool DumpPolls = false;
00028 static bool DumpDateTime = false;
00029 
00030 #define dbgprotocol(a...) do { if (DebugProtocol) fprintf(stderr, a); } while (0)
00031 
00032 // --- Helper functions ------------------------------------------------------
00033 
00034 #define SIZE_INDICATOR 0x80
00035 
00036 static const uint8_t *GetLength(const uint8_t *Data, int &Length)
00040 {
00041   Length = *Data++;
00042   if ((Length & SIZE_INDICATOR) != 0) {
00043      int l = Length & ~SIZE_INDICATOR;
00044      Length = 0;
00045      for (int i = 0; i < l; i++)
00046          Length = (Length << 8) | *Data++;
00047      }
00048   return Data;
00049 }
00050 
00051 static uint8_t *SetLength(uint8_t *Data, int Length)
00054 {
00055   uint8_t *p = Data;
00056   if (Length < 128)
00057      *p++ = Length;
00058   else {
00059      int n = sizeof(Length);
00060      for (int i = n - 1; i >= 0; i--) {
00061          int b = (Length >> (8 * i)) & 0xFF;
00062          if (p != Data || b)
00063             *++p = b;
00064          }
00065      *Data = (p - Data) | SIZE_INDICATOR;
00066      p++;
00067      }
00068   return p;
00069 }
00070 
00071 static char *CopyString(int Length, const uint8_t *Data)
00074 {
00075   // Some CAMs send funny characters at the beginning of strings.
00076   // Let's just skip them:
00077   while (Length > 0 && (*Data == ' ' || *Data == 0x05 || *Data == 0x96 || *Data == 0x97)) {
00078         Length--;
00079         Data++;
00080         }
00081   char *s = MALLOC(char, Length + 1);
00082   strncpy(s, (char *)Data, Length);
00083   s[Length] = 0;
00084   // The character 0x8A is used as newline, so let's put a real '\n' in there:
00085   strreplace(s, 0x8A, '\n');
00086   return s;
00087 }
00088 
00089 static char *GetString(int &Length, const uint8_t **Data)
00093 {
00094   if (Length > 0 && Data && *Data) {
00095      int l = 0;
00096      const uint8_t *d = GetLength(*Data, l);
00097      char *s = CopyString(l, d);
00098      Length -= d - *Data + l;
00099      *Data = d + l;
00100      return s;
00101      }
00102   return NULL;
00103 }
00104 
00105 // --- cTPDU -----------------------------------------------------------------
00106 
00107 #define MAX_TPDU_SIZE  2048
00108 #define MAX_TPDU_DATA  (MAX_TPDU_SIZE - 4)
00109 
00110 #define DATA_INDICATOR 0x80
00111 
00112 #define T_SB           0x80
00113 #define T_RCV          0x81
00114 #define T_CREATE_TC    0x82
00115 #define T_CTC_REPLY    0x83
00116 #define T_DELETE_TC    0x84
00117 #define T_DTC_REPLY    0x85
00118 #define T_REQUEST_TC   0x86
00119 #define T_NEW_TC       0x87
00120 #define T_TC_ERROR     0x88
00121 #define T_DATA_LAST    0xA0
00122 #define T_DATA_MORE    0xA1
00123 
00124 class cTPDU {
00125 private:
00126   int size;
00127   uint8_t buffer[MAX_TPDU_SIZE];
00128   const uint8_t *GetData(const uint8_t *Data, int &Length);
00129 public:
00130   cTPDU(void) { size = 0; }
00131   cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length = 0, const uint8_t *Data = NULL);
00132   uint8_t Slot(void) { return buffer[0]; }
00133   uint8_t Tcid(void) { return buffer[1]; }
00134   uint8_t Tag(void)  { return buffer[2]; }
00135   const uint8_t *Data(int &Length) { return GetData(buffer + 3, Length); }
00136   uint8_t Status(void);
00137   uint8_t *Buffer(void) { return buffer; }
00138   int Size(void) { return size; }
00139   void SetSize(int Size) { size = Size; }
00140   int MaxSize(void) { return sizeof(buffer); }
00141   void Dump(int SlotNumber, bool Outgoing);
00142   };
00143 
00144 cTPDU::cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length, const uint8_t *Data)
00145 {
00146   size = 0;
00147   buffer[0] = Slot;
00148   buffer[1] = Tcid;
00149   buffer[2] = Tag;
00150   switch (Tag) {
00151     case T_RCV:
00152     case T_CREATE_TC:
00153     case T_CTC_REPLY:
00154     case T_DELETE_TC:
00155     case T_DTC_REPLY:
00156     case T_REQUEST_TC:
00157          buffer[3] = 1; // length
00158          buffer[4] = Tcid;
00159          size = 5;
00160          break;
00161     case T_NEW_TC:
00162     case T_TC_ERROR:
00163          if (Length == 1) {
00164             buffer[3] = 2; // length
00165             buffer[4] = Tcid;
00166             buffer[5] = Data[0];
00167             size = 6;
00168             }
00169          else
00170             esyslog("ERROR: invalid data length for TPDU tag 0x%02X: %d (%d/%d)", Tag, Length, Slot, Tcid);
00171          break;
00172     case T_DATA_LAST:
00173     case T_DATA_MORE:
00174          if (Length <= MAX_TPDU_DATA) {
00175             uint8_t *p = buffer + 3;
00176             p = SetLength(p, Length + 1);
00177             *p++ = Tcid;
00178             if (Length)
00179                memcpy(p, Data, Length);
00180             size = Length + (p - buffer);
00181             }
00182          else
00183             esyslog("ERROR: invalid data length for TPDU tag 0x%02X: %d (%d/%d)", Tag, Length, Slot, Tcid);
00184          break;
00185     default:
00186          esyslog("ERROR: unknown TPDU tag: 0x%02X (%d/%d)", Tag, Slot, Tcid);
00187     }
00188  }
00189 
00190 void cTPDU::Dump(int SlotNumber, bool Outgoing)
00191 {
00192   if (DumpTPDUDataTransfer && (DumpPolls || Tag() != T_SB)) {
00193 #define MAX_DUMP 256
00194      fprintf(stderr, "     %d: %s ", SlotNumber, Outgoing ? "-->" : "<--");
00195      for (int i = 0; i < size && i < MAX_DUMP; i++)
00196          fprintf(stderr, "%02X ", buffer[i]);
00197      fprintf(stderr, "%s\n", size >= MAX_DUMP ? "..." : "");
00198      if (!Outgoing) {
00199         fprintf(stderr, "           ");
00200         for (int i = 0; i < size && i < MAX_DUMP; i++)
00201             fprintf(stderr, "%2c ", isprint(buffer[i]) ? buffer[i] : '.');
00202         fprintf(stderr, "%s\n", size >= MAX_DUMP ? "..." : "");
00203         }
00204      }
00205 }
00206 
00207 const uint8_t *cTPDU::GetData(const uint8_t *Data, int &Length)
00208 {
00209   if (size) {
00210      Data = GetLength(Data, Length);
00211      if (Length) {
00212         Length--; // the first byte is always the tcid
00213         return Data + 1;
00214         }
00215      }
00216   return NULL;
00217 }
00218 
00219 uint8_t cTPDU::Status(void)
00220 {
00221   if (size >= 4 && buffer[size - 4] == T_SB && buffer[size - 3] == 2)
00222      return buffer[size - 1];
00223   return 0;
00224 }
00225 
00226 // --- cCiTransportConnection ------------------------------------------------
00227 
00228 #define MAX_SESSIONS_PER_TC  16
00229 
00230 class cCiTransportConnection {
00231 private:
00232   enum eState { stIDLE, stCREATION, stACTIVE, stDELETION };
00233   cCamSlot *camSlot;
00234   uint8_t tcid;
00235   eState state;
00236   bool createConnectionRequested;
00237   bool deleteConnectionRequested;
00238   bool hasUserIO;
00239   cTimeMs alive;
00240   cTimeMs timer;
00241   cCiSession *sessions[MAX_SESSIONS_PER_TC + 1]; // session numbering starts with 1
00242   void SendTPDU(uint8_t Tag, int Length = 0, const uint8_t *Data = NULL);
00243   void SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId = 0, int Status = -1);
00244   void Poll(void);
00245   uint32_t ResourceIdToInt(const uint8_t *Data);
00246   cCiSession *GetSessionBySessionId(uint16_t SessionId);
00247   void OpenSession(int Length, const uint8_t *Data);
00248   void CloseSession(uint16_t SessionId);
00249   void HandleSessions(cTPDU *TPDU);
00250 public:
00251   cCiTransportConnection(cCamSlot *CamSlot, uint8_t Tcid);
00252   virtual ~cCiTransportConnection();
00253   cCamSlot *CamSlot(void) { return camSlot; }
00254   uint8_t Tcid(void) const { return tcid; }
00255   void CreateConnection(void) { createConnectionRequested = true; }
00256   void DeleteConnection(void) { deleteConnectionRequested = true; }
00257   const char *GetCamName(void);
00258   bool Ready(void);
00259   bool HasUserIO(void) { return hasUserIO; }
00260   void SendData(int Length, const uint8_t *Data);
00261   bool Process(cTPDU *TPDU = NULL);
00262   cCiSession *GetSessionByResourceId(uint32_t ResourceId);
00263   };
00264 
00265 // --- cCiSession ------------------------------------------------------------
00266 
00267 // Session Tags:
00268 
00269 #define ST_SESSION_NUMBER           0x90
00270 #define ST_OPEN_SESSION_REQUEST     0x91
00271 #define ST_OPEN_SESSION_RESPONSE    0x92
00272 #define ST_CREATE_SESSION           0x93
00273 #define ST_CREATE_SESSION_RESPONSE  0x94
00274 #define ST_CLOSE_SESSION_REQUEST    0x95
00275 #define ST_CLOSE_SESSION_RESPONSE   0x96
00276 
00277 // Session Status:
00278 
00279 #define SS_OK             0x00
00280 #define SS_NOT_ALLOCATED  0xF0
00281 
00282 // Resource Identifiers:
00283 
00284 #define RI_RESOURCE_MANAGER            0x00010041
00285 #define RI_APPLICATION_INFORMATION     0x00020041
00286 #define RI_CONDITIONAL_ACCESS_SUPPORT  0x00030041
00287 #define RI_HOST_CONTROL                0x00200041
00288 #define RI_DATE_TIME                   0x00240041
00289 #define RI_MMI                         0x00400041
00290 
00291 // Application Object Tags:
00292 
00293 #define AOT_NONE                    0x000000
00294 #define AOT_PROFILE_ENQ             0x9F8010
00295 #define AOT_PROFILE                 0x9F8011
00296 #define AOT_PROFILE_CHANGE          0x9F8012
00297 #define AOT_APPLICATION_INFO_ENQ    0x9F8020
00298 #define AOT_APPLICATION_INFO        0x9F8021
00299 #define AOT_ENTER_MENU              0x9F8022
00300 #define AOT_CA_INFO_ENQ             0x9F8030
00301 #define AOT_CA_INFO                 0x9F8031
00302 #define AOT_CA_PMT                  0x9F8032
00303 #define AOT_CA_PMT_REPLY            0x9F8033
00304 #define AOT_TUNE                    0x9F8400
00305 #define AOT_REPLACE                 0x9F8401
00306 #define AOT_CLEAR_REPLACE           0x9F8402
00307 #define AOT_ASK_RELEASE             0x9F8403
00308 #define AOT_DATE_TIME_ENQ           0x9F8440
00309 #define AOT_DATE_TIME               0x9F8441
00310 #define AOT_CLOSE_MMI               0x9F8800
00311 #define AOT_DISPLAY_CONTROL         0x9F8801
00312 #define AOT_DISPLAY_REPLY           0x9F8802
00313 #define AOT_TEXT_LAST               0x9F8803
00314 #define AOT_TEXT_MORE               0x9F8804
00315 #define AOT_KEYPAD_CONTROL          0x9F8805
00316 #define AOT_KEYPRESS                0x9F8806
00317 #define AOT_ENQ                     0x9F8807
00318 #define AOT_ANSW                    0x9F8808
00319 #define AOT_MENU_LAST               0x9F8809
00320 #define AOT_MENU_MORE               0x9F880A
00321 #define AOT_MENU_ANSW               0x9F880B
00322 #define AOT_LIST_LAST               0x9F880C
00323 #define AOT_LIST_MORE               0x9F880D
00324 #define AOT_SUBTITLE_SEGMENT_LAST   0x9F880E
00325 #define AOT_SUBTITLE_SEGMENT_MORE   0x9F880F
00326 #define AOT_DISPLAY_MESSAGE         0x9F8810
00327 #define AOT_SCENE_END_MARK          0x9F8811
00328 #define AOT_SCENE_DONE              0x9F8812
00329 #define AOT_SCENE_CONTROL           0x9F8813
00330 #define AOT_SUBTITLE_DOWNLOAD_LAST  0x9F8814
00331 #define AOT_SUBTITLE_DOWNLOAD_MORE  0x9F8815
00332 #define AOT_FLUSH_DOWNLOAD          0x9F8816
00333 #define AOT_DOWNLOAD_REPLY          0x9F8817
00334 #define AOT_COMMS_CMD               0x9F8C00
00335 #define AOT_CONNECTION_DESCRIPTOR   0x9F8C01
00336 #define AOT_COMMS_REPLY             0x9F8C02
00337 #define AOT_COMMS_SEND_LAST         0x9F8C03
00338 #define AOT_COMMS_SEND_MORE         0x9F8C04
00339 #define AOT_COMMS_RCV_LAST          0x9F8C05
00340 #define AOT_COMMS_RCV_MORE          0x9F8C06
00341 
00342 class cCiSession {
00343 private:
00344   uint16_t sessionId;
00345   uint32_t resourceId;
00346   cCiTransportConnection *tc;
00347 protected:
00348   int GetTag(int &Length, const uint8_t **Data);
00349   const uint8_t *GetData(const uint8_t *Data, int &Length);
00350   void SendData(int Tag, int Length = 0, const uint8_t *Data = NULL);
00351   cCiTransportConnection *Tc(void) { return tc; }
00352 public:
00353   cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc);
00354   virtual ~cCiSession();
00355   uint16_t SessionId(void) { return sessionId; }
00356   uint32_t ResourceId(void) { return resourceId; }
00357   virtual bool HasUserIO(void) { return false; }
00358   virtual void Process(int Length = 0, const uint8_t *Data = NULL);
00359   };
00360 
00361 cCiSession::cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc)
00362 {
00363   sessionId = SessionId;
00364   resourceId = ResourceId;
00365   tc = Tc;
00366 }
00367 
00368 cCiSession::~cCiSession()
00369 {
00370 }
00371 
00372 int cCiSession::GetTag(int &Length, const uint8_t **Data)
00376 {
00377   if (Length >= 3 && Data && *Data) {
00378      int t = 0;
00379      for (int i = 0; i < 3; i++)
00380          t = (t << 8) | *(*Data)++;
00381      Length -= 3;
00382      return t;
00383      }
00384   return AOT_NONE;
00385 }
00386 
00387 const uint8_t *cCiSession::GetData(const uint8_t *Data, int &Length)
00388 {
00389   Data = GetLength(Data, Length);
00390   return Length ? Data : NULL;
00391 }
00392 
00393 void cCiSession::SendData(int Tag, int Length, const uint8_t *Data)
00394 {
00395   uint8_t buffer[2048];
00396   uint8_t *p = buffer;
00397   *p++ = ST_SESSION_NUMBER;
00398   *p++ = 0x02;
00399   *p++ = (sessionId >> 8) & 0xFF;
00400   *p++ =  sessionId       & 0xFF;
00401   *p++ = (Tag >> 16) & 0xFF;
00402   *p++ = (Tag >>  8) & 0xFF;
00403   *p++ =  Tag        & 0xFF;
00404   p = SetLength(p, Length);
00405   if (p - buffer + Length < int(sizeof(buffer))) {
00406      memcpy(p, Data, Length);
00407      p += Length;
00408      tc->SendData(p - buffer, buffer);
00409      }
00410   else
00411      esyslog("ERROR: CAM %d: data length (%d) exceeds buffer size", Tc()->CamSlot()->SlotNumber(), Length);
00412 }
00413 
00414 void cCiSession::Process(int Length, const uint8_t *Data)
00415 {
00416 }
00417 
00418 // --- cCiResourceManager ----------------------------------------------------
00419 
00420 class cCiResourceManager : public cCiSession {
00421 private:
00422   int state;
00423 public:
00424   cCiResourceManager(uint16_t SessionId, cCiTransportConnection *Tc);
00425   virtual void Process(int Length = 0, const uint8_t *Data = NULL);
00426   };
00427 
00428 cCiResourceManager::cCiResourceManager(uint16_t SessionId, cCiTransportConnection *Tc)
00429 :cCiSession(SessionId, RI_RESOURCE_MANAGER, Tc)
00430 {
00431   dbgprotocol("Slot %d: new Resource Manager (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
00432   state = 0;
00433 }
00434 
00435 void cCiResourceManager::Process(int Length, const uint8_t *Data)
00436 {
00437   if (Data) {
00438      int Tag = GetTag(Length, &Data);
00439      switch (Tag) {
00440        case AOT_PROFILE_ENQ: {
00441             dbgprotocol("Slot %d: <== Profile Enquiry (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
00442             uint32_t resources[] = { htonl(RI_RESOURCE_MANAGER),
00443                                      htonl(RI_APPLICATION_INFORMATION),
00444                                      htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
00445                                      htonl(RI_DATE_TIME),
00446                                      htonl(RI_MMI)
00447                                    };
00448             dbgprotocol("Slot %d: ==> Profile (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
00449             SendData(AOT_PROFILE, sizeof(resources), (uint8_t*)resources);
00450             state = 3;
00451             }
00452             break;
00453        case AOT_PROFILE: {
00454             dbgprotocol("Slot %d: <== Profile (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
00455             if (state == 1) {
00456                int l = 0;
00457                const uint8_t *d = GetData(Data, l);
00458                if (l > 0 && d)
00459                   esyslog("ERROR: CAM %d: resource manager: unexpected data", Tc()->CamSlot()->SlotNumber());
00460                dbgprotocol("Slot %d: ==> Profile Change (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
00461                SendData(AOT_PROFILE_CHANGE);
00462                state = 2;
00463                }
00464             else {
00465                esyslog("ERROR: CAM %d: resource manager: unexpected tag %06X in state %d", Tc()->CamSlot()->SlotNumber(), Tag, state);
00466                }
00467             }
00468             break;
00469        default: esyslog("ERROR: CAM %d: resource manager: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
00470        }
00471      }
00472   else if (state == 0) {
00473      dbgprotocol("Slot %d: ==> Profile Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
00474      SendData(AOT_PROFILE_ENQ);
00475      state = 1;
00476      }
00477 }
00478 
00479 // --- cCiApplicationInformation ---------------------------------------------
00480 
00481 class cCiApplicationInformation : public cCiSession {
00482 private:
00483   int state;
00484   uint8_t applicationType;
00485   uint16_t applicationManufacturer;
00486   uint16_t manufacturerCode;
00487   char *menuString;
00488 public:
00489   cCiApplicationInformation(uint16_t SessionId, cCiTransportConnection *Tc);
00490   virtual ~cCiApplicationInformation();
00491   virtual void Process(int Length = 0, const uint8_t *Data = NULL);
00492   bool EnterMenu(void);
00493   const char *GetMenuString(void) { return menuString; }
00494   };
00495 
00496 cCiApplicationInformation::cCiApplicationInformation(uint16_t SessionId, cCiTransportConnection *Tc)
00497 :cCiSession(SessionId, RI_APPLICATION_INFORMATION, Tc)
00498 {
00499   dbgprotocol("Slot %d: new Application Information (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
00500   state = 0;
00501   menuString = NULL;
00502 }
00503 
00504 cCiApplicationInformation::~cCiApplicationInformation()
00505 {
00506   free(menuString);
00507 }
00508 
00509 void cCiApplicationInformation::Process(int Length, const uint8_t *Data)
00510 {
00511   if (Data) {
00512      int Tag = GetTag(Length, &Data);
00513      switch (Tag) {
00514        case AOT_APPLICATION_INFO: {
00515             dbgprotocol("Slot %d: <== Application Info (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
00516             int l = 0;
00517             const uint8_t *d = GetData(Data, l);
00518             if ((l -= 1) < 0) break;
00519             applicationType = *d++;
00520             if ((l -= 2) < 0) break;
00521             applicationManufacturer = ntohs(get_unaligned((uint16_t *)d));
00522             d += 2;
00523             if ((l -= 2) < 0) break;
00524             manufacturerCode = ntohs(get_unaligned((uint16_t *)d));
00525             d += 2;
00526             free(menuString);
00527             menuString = GetString(l, &d);
00528             isyslog("CAM %d: %s, %02X, %04X, %04X", Tc()->CamSlot()->SlotNumber(), menuString, applicationType, applicationManufacturer, manufacturerCode);
00529             state = 2;
00530             }
00531             break;
00532        default: esyslog("ERROR: CAM %d: application information: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
00533        }
00534      }
00535   else if (state == 0) {
00536      dbgprotocol("Slot %d: ==> Application Info Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
00537      SendData(AOT_APPLICATION_INFO_ENQ);
00538      state = 1;
00539      }
00540 }
00541 
00542 bool cCiApplicationInformation::EnterMenu(void)
00543 {
00544   if (state == 2) {
00545      dbgprotocol("Slot %d: ==> Enter Menu (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
00546      SendData(AOT_ENTER_MENU);
00547      return true;
00548      }
00549   return false;
00550 }
00551 
00552 // --- cCiCaPmt --------------------------------------------------------------
00553 
00554 #define MAXCASYSTEMIDS 64
00555 
00556 // Ca Pmt List Management:
00557 
00558 #define CPLM_MORE    0x00
00559 #define CPLM_FIRST   0x01
00560 #define CPLM_LAST    0x02
00561 #define CPLM_ONLY    0x03
00562 #define CPLM_ADD     0x04
00563 #define CPLM_UPDATE  0x05
00564 
00565 // Ca Pmt Cmd Ids:
00566 
00567 #define CPCI_OK_DESCRAMBLING  0x01
00568 #define CPCI_OK_MMI           0x02
00569 #define CPCI_QUERY            0x03
00570 #define CPCI_NOT_SELECTED     0x04
00571 
00572 class cCiCaPmt : public cListObject {
00573   friend class cCiConditionalAccessSupport;
00574 private:
00575   uint8_t cmdId;
00576   int length;
00577   int esInfoLengthPos;
00578   uint8_t capmt[2048]; 
00579   int source;
00580   int transponder;
00581   int programNumber;
00582   int caSystemIds[MAXCASYSTEMIDS + 1]; // list is zero terminated!
00583   void AddCaDescriptors(int Length, const uint8_t *Data);
00584 public:
00585   cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds);
00586   uint8_t CmdId(void) { return cmdId; }
00587   void SetListManagement(uint8_t ListManagement);
00588   uint8_t ListManagement(void) { return capmt[0]; }
00589   void AddPid(int Pid, uint8_t StreamType);
00590   };
00591 
00592 cCiCaPmt::cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds)
00593 {
00594   cmdId = CmdId;
00595   source = Source;
00596   transponder = Transponder;
00597   programNumber = ProgramNumber;
00598   int i = 0;
00599   if (CaSystemIds) {
00600      for (; CaSystemIds[i]; i++)
00601          caSystemIds[i] = CaSystemIds[i];
00602      }
00603   caSystemIds[i] = 0;
00604   uint8_t caDescriptors[512];
00605   int caDescriptorsLength = GetCaDescriptors(source, transponder, programNumber, caSystemIds, sizeof(caDescriptors), caDescriptors, 0);
00606   length = 0;
00607   capmt[length++] = CPLM_ONLY;
00608   capmt[length++] = (ProgramNumber >> 8) & 0xFF;
00609   capmt[length++] =  ProgramNumber       & 0xFF;
00610   capmt[length++] = 0x01; // version_number, current_next_indicator - apparently vn doesn't matter, but cni must be 1
00611   esInfoLengthPos = length;
00612   capmt[length++] = 0x00; // program_info_length H (at program level)
00613   capmt[length++] = 0x00; // program_info_length L
00614   AddCaDescriptors(caDescriptorsLength, caDescriptors);
00615 }
00616 
00617 void cCiCaPmt::SetListManagement(uint8_t ListManagement)
00618 {
00619   capmt[0] = ListManagement;
00620 }
00621 
00622 void cCiCaPmt::AddPid(int Pid, uint8_t StreamType)
00623 {
00624   if (Pid) {
00625      uint8_t caDescriptors[512];
00626      int caDescriptorsLength = GetCaDescriptors(source, transponder, programNumber, caSystemIds, sizeof(caDescriptors), caDescriptors, Pid);
00627      //XXX buffer overflow check???
00628      capmt[length++] = StreamType;
00629      capmt[length++] = (Pid >> 8) & 0xFF;
00630      capmt[length++] =  Pid       & 0xFF;
00631      esInfoLengthPos = length;
00632      capmt[length++] = 0x00; // ES_info_length H (at ES level)
00633      capmt[length++] = 0x00; // ES_info_length L
00634      AddCaDescriptors(caDescriptorsLength, caDescriptors);
00635      }
00636 }
00637 
00638 void cCiCaPmt::AddCaDescriptors(int Length, const uint8_t *Data)
00639 {
00640   if (esInfoLengthPos) {
00641      if (length + Length < int(sizeof(capmt))) {
00642         if (Length || cmdId == CPCI_QUERY) {
00643            capmt[length++] = cmdId;
00644            memcpy(capmt + length, Data, Length);
00645            length += Length;
00646            int l = length - esInfoLengthPos - 2;
00647            capmt[esInfoLengthPos]     = (l >> 8) & 0xFF;
00648            capmt[esInfoLengthPos + 1] =  l       & 0xFF;
00649            }
00650         }
00651      else
00652         esyslog("ERROR: buffer overflow in CA descriptor");
00653      esInfoLengthPos = 0;
00654      }
00655   else
00656      esyslog("ERROR: adding CA descriptor without Pid!");
00657 }
00658 
00659 // --- cCiConditionalAccessSupport -------------------------------------------
00660 
00661 // CA Enable Ids:
00662 
00663 #define CAEI_POSSIBLE                  0x01
00664 #define CAEI_POSSIBLE_COND_PURCHASE    0x02
00665 #define CAEI_POSSIBLE_COND_TECHNICAL   0x03
00666 #define CAEI_NOT_POSSIBLE_ENTITLEMENT  0x71
00667 #define CAEI_NOT_POSSIBLE_TECHNICAL    0x73
00668 
00669 #define CA_ENABLE_FLAG                 0x80
00670 
00671 #define CA_ENABLE(x) (((x) & CA_ENABLE_FLAG) ? (x) & ~CA_ENABLE_FLAG : 0)
00672 
00673 #define QUERY_WAIT_TIME      1000 // ms to wait before sending a query
00674 #define QUERY_REPLY_TIMEOUT  2000 // ms to wait for a reply to a query
00675 
00676 class cCiConditionalAccessSupport : public cCiSession {
00677 private:
00678   int state;
00679   int numCaSystemIds;
00680   int caSystemIds[MAXCASYSTEMIDS + 1]; // list is zero terminated!
00681   bool repliesToQuery;
00682   cTimeMs timer;
00683 public:
00684   cCiConditionalAccessSupport(uint16_t SessionId, cCiTransportConnection *Tc);
00685   virtual void Process(int Length = 0, const uint8_t *Data = NULL);
00686   const int *GetCaSystemIds(void) { return caSystemIds; }
00687   void SendPMT(cCiCaPmt *CaPmt);
00688   bool RepliesToQuery(void) { return repliesToQuery; }
00689   bool Ready(void) { return state >= 4; }
00690   bool ReceivedReply(void) { return state >= 5; }
00691   bool CanDecrypt(void) { return state == 6; }
00692   };
00693 
00694 cCiConditionalAccessSupport::cCiConditionalAccessSupport(uint16_t SessionId, cCiTransportConnection *Tc)
00695 :cCiSession(SessionId, RI_CONDITIONAL_ACCESS_SUPPORT, Tc)
00696 {
00697   dbgprotocol("Slot %d: new Conditional Access Support (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
00698   state = 0; // inactive
00699   caSystemIds[numCaSystemIds = 0] = 0;
00700   repliesToQuery = false;
00701 }
00702 
00703 void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
00704 {
00705   if (Data) {
00706      int Tag = GetTag(Length, &Data);
00707      switch (Tag) {
00708        case AOT_CA_INFO: {
00709             dbgprotocol("Slot %d: <== Ca Info (%d)", Tc()->CamSlot()->SlotNumber(), SessionId());
00710             numCaSystemIds = 0;
00711             int l = 0;
00712             const uint8_t *d = GetData(Data, l);
00713             while (l > 1) {
00714                   uint16_t id = ((uint16_t)(*d) << 8) | *(d + 1);
00715                   dbgprotocol(" %04X", id);
00716                   d += 2;
00717                   l -= 2;
00718                   if (numCaSystemIds < MAXCASYSTEMIDS)
00719                      caSystemIds[numCaSystemIds++] = id;
00720                   else {
00721                      esyslog("ERROR: CAM %d: too many CA system IDs!", Tc()->CamSlot()->SlotNumber());
00722                      break;
00723                      }
00724                   }
00725             caSystemIds[numCaSystemIds] = 0;
00726             dbgprotocol("\n");
00727             if (state == 1) {
00728                timer.Set(QUERY_WAIT_TIME); // WORKAROUND: Alphacrypt 3.09 doesn't reply to QUERY immediately after reset
00729                state = 2; // got ca info
00730                }
00731             }
00732             break;
00733        case AOT_CA_PMT_REPLY: {
00734             dbgprotocol("Slot %d: <== Ca Pmt Reply (%d)", Tc()->CamSlot()->SlotNumber(), SessionId());
00735             if (!repliesToQuery) {
00736                dsyslog("CAM %d: replies to QUERY - multi channel decryption possible", Tc()->CamSlot()->SlotNumber());
00737                repliesToQuery = true;
00738                }
00739             state = 5; // got ca pmt reply
00740             int l = 0;
00741             const uint8_t *d = GetData(Data, l);
00742             if (l > 1) {
00743                uint16_t pnr = ((uint16_t)(*d) << 8) | *(d + 1);
00744                dbgprotocol(" %d", pnr);
00745                d += 2;
00746                l -= 2;
00747                if (l > 0) {
00748                   dbgprotocol(" %02X", *d);
00749                   d += 1;
00750                   l -= 1;
00751                   if (l > 0) {
00752                      if (l % 3 == 0 && l > 1) {
00753                         // The EN50221 standard defines that the next byte is supposed
00754                         // to be the CA_enable value at programme level. However, there are
00755                         // CAMs (for instance the AlphaCrypt with firmware <= 3.05) that
00756                         // insert a two byte length field here.
00757                         // This is a workaround to skip this length field:
00758                         uint16_t len = ((uint16_t)(*d) << 8) | *(d + 1);
00759                         if (len == l - 2) {
00760                            d += 2;
00761                            l -= 2;
00762                            }
00763                         }
00764                      unsigned char caepl = *d;
00765                      dbgprotocol(" %02X", caepl);
00766                      d += 1;
00767                      l -= 1;
00768                      bool ok = true;
00769                      if (l <= 2)
00770                         ok = CA_ENABLE(caepl) == CAEI_POSSIBLE;
00771                      while (l > 2) {
00772                            uint16_t pid = ((uint16_t)(*d) << 8) | *(d + 1);
00773                            unsigned char caees = *(d + 2);
00774                            dbgprotocol(" %d=%02X", pid, caees);
00775                            d += 3;
00776                            l -= 3;
00777                            if (CA_ENABLE(caees) != CAEI_POSSIBLE)
00778                               ok = false;
00779                            }
00780                      if (ok)
00781                         state = 6; // descrambling possible
00782                      }
00783                   }
00784                }
00785             dbgprotocol("\n");
00786             }
00787             break;
00788        default: esyslog("ERROR: CAM %d: conditional access support: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
00789        }
00790      }
00791   else if (state == 0) {
00792      dbgprotocol("Slot %d: ==> Ca Info Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
00793      SendData(AOT_CA_INFO_ENQ);
00794      state = 1; // enquired ca info
00795      }
00796   else if (state == 2 && timer.TimedOut()) {
00797      cCiCaPmt CaPmt(CPCI_QUERY, 0, 0, 0, NULL);
00798      SendPMT(&CaPmt);
00799      timer.Set(QUERY_REPLY_TIMEOUT);
00800      state = 3; // waiting for reply
00801      }
00802   else if (state == 3 && timer.TimedOut()) {
00803      dsyslog("CAM %d: doesn't reply to QUERY - only a single channel can be decrypted", Tc()->CamSlot()->SlotNumber());
00804      state = 4; // normal operation
00805      }
00806 }
00807 
00808 void cCiConditionalAccessSupport::SendPMT(cCiCaPmt *CaPmt)
00809 {
00810   if (CaPmt && state >= 2) {
00811      dbgprotocol("Slot %d: ==> Ca Pmt (%d) %d %d\n", Tc()->CamSlot()->SlotNumber(), SessionId(), CaPmt->ListManagement(), CaPmt->CmdId());
00812      SendData(AOT_CA_PMT, CaPmt->length, CaPmt->capmt);
00813      state = 4; // sent ca pmt
00814      }
00815 }
00816 
00817 // --- cCiDateTime -----------------------------------------------------------
00818 
00819 class cCiDateTime : public cCiSession {
00820 private:
00821   int interval;
00822   time_t lastTime;
00823   void SendDateTime(void);
00824 public:
00825   cCiDateTime(uint16_t SessionId, cCiTransportConnection *Tc);
00826   virtual void Process(int Length = 0, const uint8_t *Data = NULL);
00827   };
00828 
00829 cCiDateTime::cCiDateTime(uint16_t SessionId, cCiTransportConnection *Tc)
00830 :cCiSession(SessionId, RI_DATE_TIME, Tc)
00831 {
00832   interval = 0;
00833   lastTime = 0;
00834   dbgprotocol("Slot %d: new Date Time (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
00835 }
00836 
00837 void cCiDateTime::SendDateTime(void)
00838 {
00839   time_t t = time(NULL);
00840   struct tm tm_gmt;
00841   struct tm tm_loc;
00842   if (gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc)) {
00843      int Y = tm_gmt.tm_year;
00844      int M = tm_gmt.tm_mon + 1;
00845      int D = tm_gmt.tm_mday;
00846      int L = (M == 1 || M == 2) ? 1 : 0;
00847      int MJD = 14956 + D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001);
00848 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
00849      struct tTime { uint16_t mjd; uint8_t h, m, s; short offset; };
00850      tTime T = { mjd : htons(MJD), h : DEC2BCD(tm_gmt.tm_hour), m : DEC2BCD(tm_gmt.tm_min), s : DEC2BCD(tm_gmt.tm_sec), offset : htons(tm_loc.tm_gmtoff / 60) };
00851      bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer;
00852      DumpTPDUDataTransfer &= DumpDateTime;
00853      if (DumpDateTime)
00854         dbgprotocol("Slot %d: ==> Date Time (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
00855      SendData(AOT_DATE_TIME, 7, (uint8_t*)&T);
00856      DumpTPDUDataTransfer = OldDumpTPDUDataTransfer;
00857      }
00858 }
00859 
00860 void cCiDateTime::Process(int Length, const uint8_t *Data)
00861 {
00862   if (Data) {
00863      int Tag = GetTag(Length, &Data);
00864      switch (Tag) {
00865        case AOT_DATE_TIME_ENQ: {
00866             interval = 0;
00867             int l = 0;
00868             const uint8_t *d = GetData(Data, l);
00869             if (l > 0)
00870                interval = *d;
00871             dbgprotocol("Slot %d: <== Date Time Enq (%d), interval = %d\n", Tc()->CamSlot()->SlotNumber(), SessionId(), interval);
00872             lastTime = time(NULL);
00873             SendDateTime();
00874             }
00875             break;
00876        default: esyslog("ERROR: CAM %d: date time: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
00877        }
00878      }
00879   else if (interval && time(NULL) - lastTime > interval) {
00880      lastTime = time(NULL);
00881      SendDateTime();
00882      }
00883 }
00884 
00885 // --- cCiMMI ----------------------------------------------------------------
00886 
00887 // Display Control Commands:
00888 
00889 #define DCC_SET_MMI_MODE                          0x01
00890 #define DCC_DISPLAY_CHARACTER_TABLE_LIST          0x02
00891 #define DCC_INPUT_CHARACTER_TABLE_LIST            0x03
00892 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS      0x04
00893 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS  0x05
00894 
00895 // MMI Modes:
00896 
00897 #define MM_HIGH_LEVEL                      0x01
00898 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS      0x02
00899 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS  0x03
00900 
00901 // Display Reply IDs:
00902 
00903 #define DRI_MMI_MODE_ACK                              0x01
00904 #define DRI_LIST_DISPLAY_CHARACTER_TABLES             0x02
00905 #define DRI_LIST_INPUT_CHARACTER_TABLES               0x03
00906 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS      0x04
00907 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS  0x05
00908 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD               0xF0
00909 #define DRI_UNKNOWN_MMI_MODE                          0xF1
00910 #define DRI_UNKNOWN_CHARACTER_TABLE                   0xF2
00911 
00912 // Enquiry Flags:
00913 
00914 #define EF_BLIND  0x01
00915 
00916 // Answer IDs:
00917 
00918 #define AI_CANCEL  0x00
00919 #define AI_ANSWER  0x01
00920 
00921 class cCiMMI : public cCiSession {
00922 private:
00923   char *GetText(int &Length, const uint8_t **Data);
00924   cCiMenu *menu, *fetchedMenu;
00925   cCiEnquiry *enquiry, *fetchedEnquiry;
00926 public:
00927   cCiMMI(uint16_t SessionId, cCiTransportConnection *Tc);
00928   virtual ~cCiMMI();
00929   virtual void Process(int Length = 0, const uint8_t *Data = NULL);
00930   virtual bool HasUserIO(void) { return menu || enquiry; }
00931   cCiMenu *Menu(bool Clear = false);
00932   cCiEnquiry *Enquiry(bool Clear = false);
00933   void SendMenuAnswer(uint8_t Selection);
00934   bool SendAnswer(const char *Text);
00935   bool SendCloseMMI(void);
00936   };
00937 
00938 cCiMMI::cCiMMI(uint16_t SessionId, cCiTransportConnection *Tc)
00939 :cCiSession(SessionId, RI_MMI, Tc)
00940 {
00941   dbgprotocol("Slot %d: new MMI (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
00942   menu = fetchedMenu = NULL;
00943   enquiry = fetchedEnquiry = NULL;
00944 }
00945 
00946 cCiMMI::~cCiMMI()
00947 {
00948   if (fetchedMenu) {
00949      cMutexLock MutexLock(fetchedMenu->mutex);
00950      fetchedMenu->mmi = NULL;
00951      }
00952   delete menu;
00953   if (fetchedEnquiry) {
00954      cMutexLock MutexLock(fetchedEnquiry->mutex);
00955      fetchedEnquiry->mmi = NULL;
00956      }
00957   delete enquiry;
00958 }
00959 
00960 char *cCiMMI::GetText(int &Length, const uint8_t **Data)
00964 {
00965   int Tag = GetTag(Length, Data);
00966   if (Tag == AOT_TEXT_LAST) {
00967      char *s = GetString(Length, Data);
00968      dbgprotocol("Slot %d: <== Text Last (%d) '%s'\n", Tc()->CamSlot()->SlotNumber(), SessionId(), s);
00969      return s;
00970      }
00971   else
00972      esyslog("ERROR: CAM %d: MMI: unexpected text tag: %06X", Tc()->CamSlot()->SlotNumber(), Tag);
00973   return NULL;
00974 }
00975 
00976 void cCiMMI::Process(int Length, const uint8_t *Data)
00977 {
00978   if (Data) {
00979      int Tag = GetTag(Length, &Data);
00980      switch (Tag) {
00981        case AOT_DISPLAY_CONTROL: {
00982             dbgprotocol("Slot %d: <== Display Control (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
00983             int l = 0;
00984             const uint8_t *d = GetData(Data, l);
00985             if (l > 0) {
00986                switch (*d) {
00987                  case DCC_SET_MMI_MODE:
00988                       if (l == 2 && *++d == MM_HIGH_LEVEL) {
00989                          struct tDisplayReply { uint8_t id; uint8_t mode; };
00990                          tDisplayReply dr = { id : DRI_MMI_MODE_ACK, mode : MM_HIGH_LEVEL };
00991                          dbgprotocol("Slot %d: ==> Display Reply (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
00992                          SendData(AOT_DISPLAY_REPLY, 2, (uint8_t *)&dr);
00993                          }
00994                       break;
00995                  default: esyslog("ERROR: CAM %d: MMI: unsupported display control command %02X", Tc()->CamSlot()->SlotNumber(), *d);
00996                  }
00997                }
00998             }
00999             break;
01000        case AOT_LIST_LAST:
01001        case AOT_MENU_LAST: {
01002             dbgprotocol("Slot %d: <== Menu Last (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
01003             delete menu;
01004             menu = new cCiMenu(this, Tag == AOT_MENU_LAST);
01005             int l = 0;
01006             const uint8_t *d = GetData(Data, l);
01007             if (l > 0) {
01008                // since the specification allows choiceNb to be undefined it is useless, so let's just skip it:
01009                d++;
01010                l--;
01011                if (l > 0) menu->titleText = GetText(l, &d);
01012                if (l > 0) menu->subTitleText = GetText(l, &d);
01013                if (l > 0) menu->bottomText = GetText(l, &d);
01014                while (l > 0) {
01015                      char *s = GetText(l, &d);
01016                      if (s) {
01017                         if (!menu->AddEntry(s))
01018                            free(s);
01019                         }
01020                      else
01021                         break;
01022                      }
01023                }
01024             }
01025             break;
01026        case AOT_ENQ: {
01027             dbgprotocol("Slot %d: <== Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
01028             delete enquiry;
01029             enquiry = new cCiEnquiry(this);
01030             int l = 0;
01031             const uint8_t *d = GetData(Data, l);
01032             if (l > 0) {
01033                uint8_t blind = *d++;
01034                //XXX GetByte()???
01035                l--;
01036                enquiry->blind = blind & EF_BLIND;
01037                enquiry->expectedLength = *d++;
01038                l--;
01039                // I really wonder why there is no text length field here...
01040                enquiry->text = CopyString(l, d);
01041                }
01042             }
01043             break;
01044        case AOT_CLOSE_MMI: {
01045             int id = -1;
01046             int delay = -1;
01047             int l = 0;
01048             const uint8_t *d = GetData(Data, l);
01049             if (l > 0) {
01050                id = *d++;
01051                if (l > 1)
01052                   delay = *d;
01053                }
01054             dbgprotocol("Slot %d: <== Close MMI (%d)  id = %02X  delay = %d\n", Tc()->CamSlot()->SlotNumber(), SessionId(), id, delay);
01055             }
01056             break;
01057        default: esyslog("ERROR: CAM %d: MMI: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
01058        }
01059      }
01060 }
01061 
01062 cCiMenu *cCiMMI::Menu(bool Clear)
01063 {
01064   if (Clear)
01065      fetchedMenu = NULL;
01066   else if (menu) {
01067      fetchedMenu = menu;
01068      menu = NULL;
01069      }
01070   return fetchedMenu;
01071 }
01072 
01073 cCiEnquiry *cCiMMI::Enquiry(bool Clear)
01074 {
01075   if (Clear)
01076      fetchedEnquiry = NULL;
01077   else if (enquiry) {
01078      fetchedEnquiry = enquiry;
01079      enquiry = NULL;
01080      }
01081   return fetchedEnquiry;
01082 }
01083 
01084 void cCiMMI::SendMenuAnswer(uint8_t Selection)
01085 {
01086   dbgprotocol("Slot %d: ==> Menu Answ (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
01087   SendData(AOT_MENU_ANSW, 1, &Selection);
01088 }
01089 
01090 bool cCiMMI::SendAnswer(const char *Text)
01091 {
01092   dbgprotocol("Slot %d: ==> Answ (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
01093   struct tAnswer { uint8_t id; char text[256]; };//XXX
01094   tAnswer answer;
01095   answer.id = Text ? AI_ANSWER : AI_CANCEL;
01096   if (Text)
01097      strncpy(answer.text, Text, sizeof(answer.text));
01098   SendData(AOT_ANSW, Text ? strlen(Text) + 1 : 1, (uint8_t *)&answer);
01099   return true;
01100 }
01101 
01102 bool cCiMMI::SendCloseMMI(void)
01103 {
01104   dbgprotocol("Slot %d: ==> Close MMI (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
01105   SendData(AOT_CLOSE_MMI, 0);
01106   return true;
01107 }
01108 
01109 // --- cCiMenu ---------------------------------------------------------------
01110 
01111 cCiMenu::cCiMenu(cCiMMI *MMI, bool Selectable)
01112 {
01113   mmi = MMI;
01114   mutex = NULL;
01115   selectable = Selectable;
01116   titleText = subTitleText = bottomText = NULL;
01117   numEntries = 0;
01118 }
01119 
01120 cCiMenu::~cCiMenu()
01121 {
01122   cMutexLock MutexLock(mutex);
01123   if (mmi)
01124      mmi->Menu(true);
01125   free(titleText);
01126   free(subTitleText);
01127   free(bottomText);
01128   for (int i = 0; i < numEntries; i++)
01129       free(entries[i]);
01130 }
01131 
01132 bool cCiMenu::AddEntry(char *s)
01133 {
01134   if (numEntries < MAX_CIMENU_ENTRIES) {
01135      entries[numEntries++] = s;
01136      return true;
01137      }
01138   return false;
01139 }
01140 
01141 bool cCiMenu::HasUpdate(void)
01142 {
01143   // If the mmi is gone, the menu shall be closed, which also qualifies as 'update'.
01144   return !mmi || mmi->HasUserIO();
01145 }
01146 
01147 void cCiMenu::Select(int Index)
01148 {
01149   cMutexLock MutexLock(mutex);
01150   if (mmi && -1 <= Index && Index < numEntries)
01151      mmi->SendMenuAnswer(Index + 1);
01152 }
01153 
01154 void cCiMenu::Cancel(void)
01155 {
01156   Select(-1);
01157 }
01158 
01159 void cCiMenu::Abort(void)
01160 {
01161   cMutexLock MutexLock(mutex);
01162   if (mmi)
01163      mmi->SendCloseMMI();
01164 }
01165 
01166 // --- cCiEnquiry ------------------------------------------------------------
01167 
01168 cCiEnquiry::cCiEnquiry(cCiMMI *MMI)
01169 {
01170   mmi = MMI;
01171   text = NULL;
01172   blind = false;
01173   expectedLength = 0;
01174 }
01175 
01176 cCiEnquiry::~cCiEnquiry()
01177 {
01178   cMutexLock MutexLock(mutex);
01179   if (mmi)
01180      mmi->Enquiry(true);
01181   free(text);
01182 }
01183 
01184 void cCiEnquiry::Reply(const char *s)
01185 {
01186   cMutexLock MutexLock(mutex);
01187   if (mmi)
01188      mmi->SendAnswer(s);
01189 }
01190 
01191 void cCiEnquiry::Cancel(void)
01192 {
01193   Reply(NULL);
01194 }
01195 
01196 void cCiEnquiry::Abort(void)
01197 {
01198   cMutexLock MutexLock(mutex);
01199   if (mmi)
01200      mmi->SendCloseMMI();
01201 }
01202 
01203 // --- cCiTransportConnection (cont'd) ---------------------------------------
01204 
01205 #define TC_POLL_TIMEOUT   300 // ms WORKAROUND: TC_POLL_TIMEOUT < 300ms doesn't work with DragonCAM
01206 #define TC_ALIVE_TIMEOUT 2000 // ms after which a transport connection is assumed dead
01207 
01208 cCiTransportConnection::cCiTransportConnection(cCamSlot *CamSlot, uint8_t Tcid)
01209 {
01210   dbgprotocol("Slot %d: creating connection %d/%d\n", CamSlot->SlotNumber(), CamSlot->SlotIndex(), Tcid);
01211   camSlot = CamSlot;
01212   tcid = Tcid;
01213   state = stIDLE;
01214   createConnectionRequested = false;
01215   deleteConnectionRequested = false;
01216   hasUserIO = false;
01217   alive.Set(TC_ALIVE_TIMEOUT);
01218   for (int i = 0; i <= MAX_SESSIONS_PER_TC; i++) // sessions[0] is not used, but initialized anyway
01219       sessions[i] = NULL;
01220 }
01221 
01222 cCiTransportConnection::~cCiTransportConnection()
01223 {
01224   for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++)
01225       delete sessions[i];
01226 }
01227 
01228 bool cCiTransportConnection::Ready(void)
01229 {
01230   cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
01231   return cas && cas->Ready();
01232 }
01233 
01234 const char *cCiTransportConnection::GetCamName(void)
01235 {
01236   cCiApplicationInformation *ai = (cCiApplicationInformation *)GetSessionByResourceId(RI_APPLICATION_INFORMATION);
01237   return ai ? ai->GetMenuString() : NULL;
01238 }
01239 
01240 void cCiTransportConnection::SendTPDU(uint8_t Tag, int Length, const uint8_t *Data)
01241 {
01242   cTPDU TPDU(camSlot->SlotIndex(), tcid, Tag, Length, Data);
01243   camSlot->Write(&TPDU);
01244   timer.Set(TC_POLL_TIMEOUT);
01245 }
01246 
01247 void cCiTransportConnection::SendData(int Length, const uint8_t *Data)
01248 {
01249   // if Length ever exceeds MAX_TPDU_DATA this needs to be handled differently
01250   if (state == stACTIVE && Length > 0)
01251      SendTPDU(T_DATA_LAST, Length, Data);
01252 }
01253 
01254 void cCiTransportConnection::SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId, int Status)
01255 {
01256   uint8_t buffer[16];
01257   uint8_t *p = buffer;
01258   *p++ = Tag;
01259   *p++ = 0x00; // will contain length
01260   if (Status >= 0)
01261      *p++ = Status;
01262   if (ResourceId) {
01263      put_unaligned(htonl(ResourceId), (uint32_t *)p);
01264      p += 4;
01265      }
01266   put_unaligned(htons(SessionId), (uint16_t *)p);
01267   p += 2;
01268   buffer[1] = p - buffer - 2; // length
01269   SendData(p - buffer, buffer);
01270 }
01271 
01272 void cCiTransportConnection::Poll(void)
01273 {
01274   bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer;
01275   DumpTPDUDataTransfer &= DumpPolls;
01276   if (DumpPolls)
01277      dbgprotocol("Slot %d: ==> Poll\n", camSlot->SlotNumber());
01278   SendTPDU(T_DATA_LAST);
01279   DumpTPDUDataTransfer = OldDumpTPDUDataTransfer;
01280 }
01281 
01282 uint32_t cCiTransportConnection::ResourceIdToInt(const uint8_t *Data)
01283 {
01284   return (ntohl(get_unaligned((uint32_t *)Data)));
01285 }
01286 
01287 cCiSession *cCiTransportConnection::GetSessionBySessionId(uint16_t SessionId)
01288 {
01289   return (SessionId <= MAX_SESSIONS_PER_TC) ? sessions[SessionId] : NULL;
01290 }
01291 
01292 cCiSession *cCiTransportConnection::GetSessionByResourceId(uint32_t ResourceId)
01293 {
01294   for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
01295       if (sessions[i] && sessions[i]->ResourceId() == ResourceId)
01296          return sessions[i];
01297       }
01298   return NULL;
01299 }
01300 
01301 void cCiTransportConnection::OpenSession(int Length, const uint8_t *Data)
01302 {
01303   if (Length == 6 && *(Data + 1) == 0x04) {
01304      uint32_t ResourceId = ResourceIdToInt(Data + 2);
01305      dbgprotocol("Slot %d: open session %08X\n", camSlot->SlotNumber(), ResourceId);
01306      if (!GetSessionByResourceId(ResourceId)) {
01307         for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
01308             if (!sessions[i]) {
01309                switch (ResourceId) {
01310                  case RI_RESOURCE_MANAGER:           sessions[i] = new cCiResourceManager(i, this); break;
01311                  case RI_APPLICATION_INFORMATION:    sessions[i] = new cCiApplicationInformation(i, this); break;
01312                  case RI_CONDITIONAL_ACCESS_SUPPORT: sessions[i] = new cCiConditionalAccessSupport(i, this); break;
01313                  case RI_DATE_TIME:                  sessions[i] = new cCiDateTime(i, this); break;
01314                  case RI_MMI:                        sessions[i] = new cCiMMI(i, this); break;
01315                  case RI_HOST_CONTROL:               // not implemented
01316                  default: esyslog("ERROR: CAM %d: unknown resource identifier: %08X (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
01317                  }
01318                if (sessions[i])
01319                   SendTag(ST_OPEN_SESSION_RESPONSE, sessions[i]->SessionId(), sessions[i]->ResourceId(), SS_OK);
01320                return;
01321                }
01322             }
01323         esyslog("ERROR: CAM %d: no free session slot for resource identifier %08X (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
01324         }
01325      else
01326         esyslog("ERROR: CAM %d: session for resource identifier %08X already exists (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
01327      }
01328 }
01329 
01330 void cCiTransportConnection::CloseSession(uint16_t SessionId)
01331 {
01332   dbgprotocol("Slot %d: close session %d\n", camSlot->SlotNumber(), SessionId);
01333   cCiSession *Session = GetSessionBySessionId(SessionId);
01334   if (Session && sessions[SessionId] == Session) {
01335      delete Session;
01336      sessions[SessionId] = NULL;
01337      SendTag(ST_CLOSE_SESSION_RESPONSE, SessionId, 0, SS_OK);
01338      }
01339   else {
01340      esyslog("ERROR: CAM %d: unknown session id: %d (%d/%d)", camSlot->SlotNumber(), SessionId, camSlot->SlotIndex(), tcid);
01341      SendTag(ST_CLOSE_SESSION_RESPONSE, SessionId, 0, SS_NOT_ALLOCATED);
01342      }
01343 }
01344 
01345 void cCiTransportConnection::HandleSessions(cTPDU *TPDU)
01346 {
01347   int Length;
01348   const uint8_t *Data = TPDU->Data(Length);
01349   if (Data && Length > 1) {
01350      switch (*Data) {
01351        case ST_SESSION_NUMBER:          if (Length > 4) {
01352                                            uint16_t SessionId = ntohs(get_unaligned((uint16_t *)&Data[2]));
01353                                            cCiSession *Session = GetSessionBySessionId(SessionId);
01354                                            if (Session)
01355                                               Session->Process(Length - 4, Data + 4);
01356                                            else
01357                                               esyslog("ERROR: CAM %d: unknown session id: %d (%d/%d)", camSlot->SlotNumber(), SessionId, camSlot->SlotIndex(), tcid);
01358                                            }
01359                                         break;
01360        case ST_OPEN_SESSION_REQUEST:    OpenSession(Length, Data);
01361                                         break;
01362        case ST_CLOSE_SESSION_REQUEST:   if (Length == 4)
01363                                            CloseSession(ntohs(get_unaligned((uint16_t *)&Data[2])));
01364                                         break;
01365        case ST_CREATE_SESSION_RESPONSE: // not implemented
01366        case ST_CLOSE_SESSION_RESPONSE:  // not implemented
01367        default: esyslog("ERROR: CAM %d: unknown session tag: %02X (%d/%d)", camSlot->SlotNumber(), *Data, camSlot->SlotIndex(), tcid);
01368        }
01369      }
01370 }
01371 
01372 bool cCiTransportConnection::Process(cTPDU *TPDU)
01373 {
01374   if (TPDU)
01375      alive.Set(TC_ALIVE_TIMEOUT);
01376   else if (alive.TimedOut())
01377      return false;
01378   switch (state) {
01379     case stIDLE:
01380          if (createConnectionRequested) {
01381             dbgprotocol("Slot %d: create connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
01382             createConnectionRequested = false;
01383             SendTPDU(T_CREATE_TC);
01384             state = stCREATION;
01385             }
01386          return true;
01387     case stCREATION:
01388          if (TPDU && TPDU->Tag() == T_CTC_REPLY) {
01389             dbgprotocol("Slot %d: connection created %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
01390             Poll();
01391             state = stACTIVE;
01392             }
01393          else if (timer.TimedOut()) {
01394             dbgprotocol("Slot %d: timeout while creating connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
01395             state = stIDLE;
01396             }
01397          return true;
01398     case stACTIVE:
01399          if (deleteConnectionRequested) {
01400             dbgprotocol("Slot %d: delete connection requested %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
01401             deleteConnectionRequested = false;
01402             SendTPDU(T_DELETE_TC);
01403             state = stDELETION;
01404             return true;
01405             }
01406          if (TPDU) {
01407             switch (TPDU->Tag()) {
01408               case T_REQUEST_TC:
01409                    esyslog("ERROR: CAM %d: T_REQUEST_TC not implemented (%d/%d)", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
01410                    break;
01411               case T_DATA_MORE:
01412               case T_DATA_LAST:
01413                    HandleSessions(TPDU);
01414                    // continue with T_SB
01415               case T_SB:
01416                    if ((TPDU->Status() & DATA_INDICATOR) != 0) {
01417                       dbgprotocol("Slot %d: receive data %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
01418                       SendTPDU(T_RCV);
01419                       }
01420                    break;
01421               case T_DELETE_TC:
01422                    dbgprotocol("Slot %d: delete connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
01423                    SendTPDU(T_DTC_REPLY);
01424                    state = stIDLE;
01425                    return true;
01426               case T_RCV:
01427               case T_CREATE_TC:
01428               case T_CTC_REPLY:
01429               case T_DTC_REPLY:
01430               case T_NEW_TC:
01431               case T_TC_ERROR:
01432                    break;
01433               default:
01434                    esyslog("ERROR: unknown TPDU tag: 0x%02X (%s)", TPDU->Tag(), __FUNCTION__);
01435               }
01436             }
01437          else if (timer.TimedOut())
01438             Poll();
01439          hasUserIO = false;
01440          for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
01441              if (sessions[i]) {
01442                 sessions[i]->Process();
01443                 if (sessions[i]->HasUserIO())
01444                    hasUserIO = true;
01445                 }
01446              }
01447          break;
01448     case stDELETION:
01449          if (TPDU && TPDU->Tag() == T_DTC_REPLY || timer.TimedOut()) {
01450             dbgprotocol("Slot %d: connection deleted %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
01451             state = stIDLE;
01452             }
01453          return true;
01454     default:
01455          esyslog("ERROR: unknown state: %d (%s)", state, __FUNCTION__);
01456     }
01457   return true;
01458 }
01459 
01460 // --- cCiCaPidData ----------------------------------------------------------
01461 
01462 class cCiCaPidData : public cListObject {
01463 public:
01464   bool active;
01465   int pid;
01466   int streamType;
01467   cCiCaPidData(int Pid, int StreamType)
01468   {
01469     active = false;
01470     pid = Pid;
01471     streamType = StreamType;
01472   }
01473   };
01474 
01475 // --- cCiCaProgramData ------------------------------------------------------
01476 
01477 class cCiCaProgramData : public cListObject {
01478 public:
01479   int programNumber;
01480   bool modified;
01481   cList<cCiCaPidData> pidList;
01482   cCiCaProgramData(int ProgramNumber)
01483   {
01484     programNumber = ProgramNumber;
01485     modified = false;
01486   }
01487   };
01488 
01489 // --- cCiAdapter ------------------------------------------------------------
01490 
01491 cCiAdapter::cCiAdapter(void)
01492 :cThread("CI adapter")
01493 {
01494   assignedDevice = NULL;
01495   for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++)
01496       camSlots[i] = NULL;
01497 }
01498 
01499 cCiAdapter::~cCiAdapter()
01500 {
01501   Cancel(3);
01502   for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++)
01503       delete camSlots[i];
01504 }
01505 
01506 void cCiAdapter::AddCamSlot(cCamSlot *CamSlot)
01507 {
01508   if (CamSlot) {
01509      for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) {
01510          if (!camSlots[i]) {
01511             CamSlot->slotIndex = i;
01512             camSlots[i] = CamSlot;
01513             return;
01514             }
01515         }
01516      esyslog("ERROR: no free CAM slot in CI adapter");
01517      }
01518 }
01519 
01520 bool cCiAdapter::Ready(void)
01521 {
01522   for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) {
01523       if (camSlots[i] && !camSlots[i]->Ready())
01524          return false;
01525       }
01526   return true;
01527 }
01528 
01529 void cCiAdapter::Action(void)
01530 {
01531   cTPDU TPDU;
01532   while (Running()) {
01533         int n = Read(TPDU.Buffer(), TPDU.MaxSize());
01534         if (n > 0 && TPDU.Slot() < MAX_CAM_SLOTS_PER_ADAPTER) {
01535            TPDU.SetSize(n);
01536            cCamSlot *cs = camSlots[TPDU.Slot()];
01537            TPDU.Dump(cs ? cs->SlotNumber() : 0, false);
01538            if (cs)
01539               cs->Process(&TPDU);
01540            }
01541         for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) {
01542             if (camSlots[i])
01543                camSlots[i]->Process();
01544             }
01545         }
01546 }
01547 
01548 // --- cCamSlot --------------------------------------------------------------
01549 
01550 cCamSlots CamSlots;
01551 
01552 #define MODULE_CHECK_INTERVAL 500 // ms
01553 #define MODULE_RESET_TIMEOUT    2 // s
01554 
01555 cCamSlot::cCamSlot(cCiAdapter *CiAdapter)
01556 {
01557   ciAdapter = CiAdapter;
01558   slotIndex = -1;
01559   lastModuleStatus = msReset; // avoids initial reset log message
01560   resetTime = 0;
01561   resendPmt = false;
01562   source = transponder = 0;
01563   for (int i = 0; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) // tc[0] is not used, but initialized anyway
01564       tc[i] = NULL;
01565   CamSlots.Add(this);
01566   slotNumber = Index() + 1;
01567   if (ciAdapter)
01568      ciAdapter->AddCamSlot(this);
01569   Reset();
01570 }
01571 
01572 cCamSlot::~cCamSlot()
01573 {
01574   CamSlots.Del(this, false);
01575   DeleteAllConnections();
01576 }
01577 
01578 bool cCamSlot::Assign(cDevice *Device, bool Query)
01579 {
01580   cMutexLock MutexLock(&mutex);
01581   if (ciAdapter) {
01582      if (ciAdapter->Assign(Device, true)) {
01583         if (!Device && ciAdapter->assignedDevice)
01584            ciAdapter->assignedDevice->SetCamSlot(NULL);
01585         if (!Query) {
01586            StopDecrypting();
01587            source = transponder = 0;
01588            if (ciAdapter->Assign(Device)) {
01589               ciAdapter->assignedDevice = Device;
01590               if (Device) {
01591                  Device->SetCamSlot(this);
01592                  dsyslog("CAM %d: assigned to device %d", slotNumber, Device->DeviceNumber() + 1);
01593                  }
01594               else
01595                  dsyslog("CAM %d: unassigned", slotNumber);
01596               }
01597            else
01598               return false;
01599            }
01600         return true;
01601         }
01602      }
01603   return false;
01604 }
01605 
01606 cDevice *cCamSlot::Device(void)
01607 {
01608   cMutexLock MutexLock(&mutex);
01609   if (ciAdapter) {
01610      cDevice *d = ciAdapter->assignedDevice;
01611      if (d && d->CamSlot() == this)
01612         return d;
01613      }
01614   return NULL;
01615 }
01616 
01617 void cCamSlot::NewConnection(void)
01618 {
01619   cMutexLock MutexLock(&mutex);
01620   for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) {
01621       if (!tc[i]) {
01622          tc[i] = new cCiTransportConnection(this, i);
01623          tc[i]->CreateConnection();
01624          return;
01625          }
01626       }
01627   esyslog("ERROR: CAM %d: can't create new transport connection!", slotNumber);
01628 }
01629 
01630 void cCamSlot::DeleteAllConnections(void)
01631 {
01632   cMutexLock MutexLock(&mutex);
01633   for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) {
01634       delete tc[i];
01635       tc[i] = NULL;
01636       }
01637 }
01638 
01639 void cCamSlot::Process(cTPDU *TPDU)
01640 {
01641   cMutexLock MutexLock(&mutex);
01642   if (TPDU) {
01643      int n = TPDU->Tcid();
01644      if (1 <= n && n <= MAX_CONNECTIONS_PER_CAM_SLOT) {
01645         if (tc[n])
01646            tc[n]->Process(TPDU);
01647         }
01648      }
01649   for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) {
01650       if (tc[i]) {
01651          if (!tc[i]->Process()) {
01652            Reset();
01653            return;
01654            }
01655          }
01656       }
01657   if (moduleCheckTimer.TimedOut()) {
01658      eModuleStatus ms = ModuleStatus();
01659      if (ms != lastModuleStatus) {
01660         switch (ms) {
01661           case msNone:
01662                dbgprotocol("Slot %d: no module present\n", slotNumber);
01663                isyslog("CAM %d: no module present", slotNumber);
01664                DeleteAllConnections();
01665                break;
01666           case msReset:
01667                dbgprotocol("Slot %d: module reset\n", slotNumber);
01668                isyslog("CAM %d: module reset", slotNumber);
01669                DeleteAllConnections();
01670                break;
01671           case msPresent:
01672                dbgprotocol("Slot %d: module present\n", slotNumber);
01673                isyslog("CAM %d: module present", slotNumber);
01674                break;
01675           case msReady:
01676                dbgprotocol("Slot %d: module ready\n", slotNumber);
01677                isyslog("CAM %d: module ready", slotNumber);
01678                NewConnection();
01679                resendPmt = caProgramList.Count() > 0;
01680                break;
01681           default:
01682                esyslog("ERROR: unknown module status %d (%s)", ms, __FUNCTION__);
01683           }
01684         lastModuleStatus = ms;
01685         }
01686      moduleCheckTimer.Set(MODULE_CHECK_INTERVAL);
01687      }
01688   if (resendPmt)
01689      SendCaPmt(CPCI_OK_DESCRAMBLING);
01690   processed.Broadcast();
01691 }
01692 
01693 cCiSession *cCamSlot::GetSessionByResourceId(uint32_t ResourceId)
01694 {
01695   cMutexLock MutexLock(&mutex);
01696   return tc[1] ? tc[1]->GetSessionByResourceId(ResourceId) : NULL;
01697 }
01698 
01699 void cCamSlot::Write(cTPDU *TPDU)
01700 {
01701   cMutexLock MutexLock(&mutex);
01702   if (ciAdapter && TPDU->Size()) {
01703      TPDU->Dump(SlotNumber(), true);
01704      ciAdapter->Write(TPDU->Buffer(), TPDU->Size());
01705      }
01706 }
01707 
01708 bool cCamSlot::Reset(void)
01709 {
01710   cMutexLock MutexLock(&mutex);
01711   ChannelCamRelations.Reset(slotNumber);
01712   DeleteAllConnections();
01713   if (ciAdapter) {
01714      dbgprotocol("Slot %d: reset...", slotNumber);
01715      if (ciAdapter->Reset(slotIndex)) {
01716         resetTime = time(NULL);
01717         dbgprotocol("ok.\n");
01718         return true;
01719         }
01720      dbgprotocol("failed!\n");
01721      }
01722   return false;
01723 }
01724 
01725 eModuleStatus cCamSlot::ModuleStatus(void)
01726 {
01727   cMutexLock MutexLock(&mutex);
01728   eModuleStatus ms = ciAdapter ? ciAdapter->ModuleStatus(slotIndex) : msNone;
01729   if (resetTime) {
01730      if (ms <= msReset) {
01731         if (time(NULL) - resetTime < MODULE_RESET_TIMEOUT)
01732            return msReset;
01733         }
01734      resetTime = 0;
01735      }
01736   return ms;
01737 }
01738 
01739 const char *cCamSlot::GetCamName(void)
01740 {
01741   cMutexLock MutexLock(&mutex);
01742   return tc[1] ? tc[1]->GetCamName() : NULL;
01743 }
01744 
01745 bool cCamSlot::Ready(void)
01746 {
01747   cMutexLock MutexLock(&mutex);
01748   return ModuleStatus() == msNone || tc[1] && tc[1]->Ready();
01749 }
01750 
01751 bool cCamSlot::HasMMI(void)
01752 {
01753   return GetSessionByResourceId(RI_MMI);
01754 }
01755 
01756 bool cCamSlot::HasUserIO(void)
01757 {
01758   cMutexLock MutexLock(&mutex);
01759   return tc[1] && tc[1]->HasUserIO();
01760 }
01761 
01762 bool cCamSlot::EnterMenu(void)
01763 {
01764   cMutexLock MutexLock(&mutex);
01765   cCiApplicationInformation *api = (cCiApplicationInformation *)GetSessionByResourceId(RI_APPLICATION_INFORMATION);
01766   return api ? api->EnterMenu() : false;
01767 }
01768 
01769 cCiMenu *cCamSlot::GetMenu(void)
01770 {
01771   cMutexLock MutexLock(&mutex);
01772   cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI);
01773   if (mmi) {
01774      cCiMenu *Menu = mmi->Menu();
01775      if (Menu)
01776         Menu->mutex = &mutex;
01777      return Menu;
01778      }
01779   return NULL;
01780 }
01781 
01782 cCiEnquiry *cCamSlot::GetEnquiry(void)
01783 {
01784   cMutexLock MutexLock(&mutex);
01785   cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI);
01786   if (mmi) {
01787      cCiEnquiry *Enquiry = mmi->Enquiry();
01788      if (Enquiry)
01789         Enquiry->mutex = &mutex;
01790      return Enquiry;
01791      }
01792   return NULL;
01793 }
01794 
01795 void cCamSlot::SendCaPmt(uint8_t CmdId)
01796 {
01797   cMutexLock MutexLock(&mutex);
01798   cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
01799   if (cas) {
01800      const int *CaSystemIds = cas->GetCaSystemIds();
01801      if (CaSystemIds && *CaSystemIds) {
01802         if (caProgramList.Count()) {
01803            for (int Loop = 1; Loop <= 2; Loop++) {
01804                for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
01805                    if (p->modified || resendPmt) {
01806                       bool Active = false;
01807                       cCiCaPmt CaPmt(CmdId, source, transponder, p->programNumber, CaSystemIds);
01808                       for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
01809                           if (q->active) {
01810                              CaPmt.AddPid(q->pid, q->streamType);
01811                              Active = true;
01812                              }
01813                           }
01814                       if ((Loop == 1) != Active) { // first remove, then add
01815                          if (cas->RepliesToQuery())
01816                             CaPmt.SetListManagement(Active ? CPLM_ADD : CPLM_UPDATE);
01817                          if (Active || cas->RepliesToQuery())
01818                             cas->SendPMT(&CaPmt);
01819                          p->modified = false;
01820                          }
01821                       }
01822                    }
01823                }
01824            resendPmt = false;
01825            }
01826         else {
01827            cCiCaPmt CaPmt(CmdId, 0, 0, 0, NULL);
01828            cas->SendPMT(&CaPmt);
01829            }
01830         }
01831      }
01832 }
01833 
01834 const int *cCamSlot::GetCaSystemIds(void)
01835 {
01836   cMutexLock MutexLock(&mutex);
01837   cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
01838   return cas ? cas->GetCaSystemIds() : NULL;
01839 }
01840 
01841 int cCamSlot::Priority(void)
01842 {
01843   cDevice *d = Device();
01844   return d ? d->Priority() : IDLEPRIORITY;
01845 }
01846 
01847 bool cCamSlot::ProvidesCa(const int *CaSystemIds)
01848 {
01849   cMutexLock MutexLock(&mutex);
01850   cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
01851   if (cas) {
01852      for (const int *ids = cas->GetCaSystemIds(); ids && *ids; ids++) {
01853          for (const int *id = CaSystemIds; *id; id++) {
01854              if (*id == *ids)
01855                 return true;
01856              }
01857          }
01858      }
01859   return false;
01860 }
01861 
01862 void cCamSlot::AddPid(int ProgramNumber, int Pid, int StreamType)
01863 {
01864   cMutexLock MutexLock(&mutex);
01865   cCiCaProgramData *ProgramData = NULL;
01866   for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
01867       if (p->programNumber == ProgramNumber) {
01868          ProgramData = p;
01869          for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
01870              if (q->pid == Pid)
01871                 return;
01872              }
01873          }
01874       }
01875   if (!ProgramData)
01876      caProgramList.Add(ProgramData = new cCiCaProgramData(ProgramNumber));
01877   ProgramData->pidList.Add(new cCiCaPidData(Pid, StreamType));
01878 }
01879 
01880 void cCamSlot::SetPid(int Pid, bool Active)
01881 {
01882   cMutexLock MutexLock(&mutex);
01883   for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
01884       for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
01885           if (q->pid == Pid) {
01886              if (q->active != Active) {
01887                 q->active = Active;
01888                 p->modified = true;
01889                 }
01890              return;
01891              }
01892          }
01893       }
01894 }
01895 
01896 // see ISO/IEC 13818-1
01897 #define STREAM_TYPE_VIDEO    0x02
01898 #define STREAM_TYPE_AUDIO    0x04
01899 #define STREAM_TYPE_PRIVATE  0x06
01900 
01901 void cCamSlot::AddChannel(const cChannel *Channel)
01902 {
01903   cMutexLock MutexLock(&mutex);
01904   if (source != Channel->Source() || transponder != Channel->Transponder())
01905      StopDecrypting();
01906   source = Channel->Source();
01907   transponder = Channel->Transponder();
01908   if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
01909      AddPid(Channel->Sid(), Channel->Vpid(), STREAM_TYPE_VIDEO);
01910      for (const int *Apid = Channel->Apids(); *Apid; Apid++)
01911          AddPid(Channel->Sid(), *Apid, STREAM_TYPE_AUDIO);
01912      for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
01913          AddPid(Channel->Sid(), *Dpid, STREAM_TYPE_PRIVATE);
01914      for (const int *Spid = Channel->Spids(); *Spid; Spid++)
01915          AddPid(Channel->Sid(), *Spid, STREAM_TYPE_PRIVATE);
01916      if (Channel->Tpid() && Setup.SupportTeletext)
01917         AddPid(Channel->Sid(), Channel->Tpid(), STREAM_TYPE_PRIVATE);
01918      }
01919 }
01920 
01921 #define QUERY_REPLY_WAIT  100 // ms to wait between checks for a reply
01922 
01923 bool cCamSlot::CanDecrypt(const cChannel *Channel)
01924 {
01925   if (Channel->Ca() < CA_ENCRYPTED_MIN)
01926      return true; // channel not encrypted
01927   if (!IsDecrypting())
01928      return true; // any CAM can decrypt at least one channel
01929   cMutexLock MutexLock(&mutex);
01930   cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT);
01931   if (cas && cas->RepliesToQuery()) {
01932      cCiCaPmt CaPmt(CPCI_QUERY, Channel->Source(), Channel->Transponder(), Channel->Sid(), GetCaSystemIds());
01933      CaPmt.SetListManagement(CPLM_ADD); // WORKAROUND: CPLM_ONLY doesn't work with Alphacrypt 3.09 (deletes existing CA_PMTs)
01934      CaPmt.AddPid(Channel->Vpid(), STREAM_TYPE_VIDEO);
01935      for (const int *Apid = Channel->Apids(); *Apid; Apid++)
01936          CaPmt.AddPid(*Apid, STREAM_TYPE_AUDIO);
01937      for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
01938          CaPmt.AddPid(*Dpid, STREAM_TYPE_PRIVATE);
01939      for (const int *Spid = Channel->Spids(); *Spid; Spid++)
01940          CaPmt.AddPid(*Spid, STREAM_TYPE_PRIVATE); 
01941      if (Channel->Tpid() && Setup.SupportTeletext) {
01942         CaPmt.AddPid(Channel->Tpid(), STREAM_TYPE_PRIVATE);
01943         }
01944      cas->SendPMT(&CaPmt);
01945      cTimeMs Timeout(QUERY_REPLY_TIMEOUT);
01946      do {
01947         processed.TimedWait(mutex, QUERY_REPLY_WAIT);
01948         if ((cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT)) != NULL) { // must re-fetch it, there might have been a reset
01949            if (cas->ReceivedReply())
01950               return cas->CanDecrypt();
01951            }
01952         else
01953            return false;
01954         } while (!Timeout.TimedOut());
01955      dsyslog("CAM %d: didn't reply to QUERY", SlotNumber());
01956      }
01957   return false;
01958 }
01959 
01960 void cCamSlot::StartDecrypting(void)
01961 {
01962   SendCaPmt(CPCI_OK_DESCRAMBLING);
01963 }
01964 
01965 void cCamSlot::StopDecrypting(void)
01966 {
01967   cMutexLock MutexLock(&mutex);
01968   if (caProgramList.Count()) {
01969      caProgramList.Clear();
01970      SendCaPmt(CPCI_NOT_SELECTED);
01971      }
01972 }
01973 
01974 bool cCamSlot::IsDecrypting(void)
01975 {
01976   cMutexLock MutexLock(&mutex);
01977   if (caProgramList.Count()) {
01978      for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
01979          if (p->modified)
01980             return true; // any modifications need to be processed before we can assume it's no longer decrypting
01981          for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
01982              if (q->active)
01983                 return true;
01984              }
01985          }
01986      }
01987   return false;
01988 }
01989 
01990 // --- cChannelCamRelation ---------------------------------------------------
01991 
01992 #define CAM_CHECKED_TIMEOUT  15 // seconds before a CAM that has been checked for a particular channel will be checked again
01993 
01994 class cChannelCamRelation : public cListObject {
01995 private:
01996   tChannelID channelID;
01997   uint32_t camSlotsChecked;
01998   uint32_t camSlotsDecrypt;
01999   time_t lastChecked;
02000 public:
02001   cChannelCamRelation(tChannelID ChannelID);
02002   bool TimedOut(void);
02003   tChannelID ChannelID(void) { return channelID; }
02004   bool CamChecked(int CamSlotNumber);
02005   bool CamDecrypt(int CamSlotNumber);
02006   void SetChecked(int CamSlotNumber);
02007   void SetDecrypt(int CamSlotNumber);
02008   void ClrChecked(int CamSlotNumber);
02009   void ClrDecrypt(int CamSlotNumber);
02010   };
02011 
02012 cChannelCamRelation::cChannelCamRelation(tChannelID ChannelID)
02013 {
02014   channelID = ChannelID;
02015   camSlotsChecked = 0;
02016   camSlotsDecrypt = 0;
02017   lastChecked = 0;
02018 }
02019 
02020 bool cChannelCamRelation::TimedOut(void)
02021 {
02022   return !camSlotsDecrypt && time(NULL) - lastChecked > CAM_CHECKED_TIMEOUT;
02023 }
02024 
02025 bool cChannelCamRelation::CamChecked(int CamSlotNumber)
02026 {
02027   if (lastChecked && time(NULL) - lastChecked > CAM_CHECKED_TIMEOUT) {
02028      lastChecked = 0;
02029      camSlotsChecked = 0;
02030      }
02031   return camSlotsChecked & (1 << (CamSlotNumber - 1));
02032 }
02033 
02034 bool cChannelCamRelation::CamDecrypt(int CamSlotNumber)
02035 {
02036   return camSlotsDecrypt & (1 << (CamSlotNumber - 1));
02037 }
02038 
02039 void cChannelCamRelation::SetChecked(int CamSlotNumber)
02040 {
02041   camSlotsChecked |= (1 << (CamSlotNumber - 1));
02042   lastChecked = time(NULL);
02043   ClrDecrypt(CamSlotNumber);
02044 }
02045 
02046 void cChannelCamRelation::SetDecrypt(int CamSlotNumber)
02047 {
02048   camSlotsDecrypt |= (1 << (CamSlotNumber - 1));
02049   ClrChecked(CamSlotNumber);
02050 }
02051 
02052 void cChannelCamRelation::ClrChecked(int CamSlotNumber)
02053 {
02054   camSlotsChecked &= ~(1 << (CamSlotNumber - 1));
02055   lastChecked = 0;
02056 }
02057 
02058 void cChannelCamRelation::ClrDecrypt(int CamSlotNumber)
02059 {
02060   camSlotsDecrypt &= ~(1 << (CamSlotNumber - 1));
02061 }
02062 
02063 // --- cChannelCamRelations --------------------------------------------------
02064 
02065 #define CHANNEL_CAM_RELATIONS_CLEANUP_INTERVAL 3600 // seconds between cleanups
02066 
02067 cChannelCamRelations ChannelCamRelations;
02068 
02069 cChannelCamRelations::cChannelCamRelations(void)
02070 {
02071   lastCleanup = time(NULL);
02072 }
02073 
02074 void cChannelCamRelations::Cleanup(void)
02075 {
02076   cMutexLock MutexLock(&mutex);
02077   if (time(NULL) - lastCleanup > CHANNEL_CAM_RELATIONS_CLEANUP_INTERVAL) {
02078      for (cChannelCamRelation *ccr = First(); ccr; ) {
02079          cChannelCamRelation *c = ccr;
02080          ccr = Next(ccr);
02081          if (c->TimedOut())
02082             Del(c);
02083          }
02084      lastCleanup = time(NULL);
02085      }
02086 }
02087 
02088 cChannelCamRelation *cChannelCamRelations::GetEntry(tChannelID ChannelID)
02089 {
02090   cMutexLock MutexLock(&mutex);
02091   Cleanup();
02092   for (cChannelCamRelation *ccr = First(); ccr; ccr = Next(ccr)) {
02093       if (ccr->ChannelID() == ChannelID)
02094          return ccr;
02095       }
02096   return NULL;
02097 }
02098 
02099 cChannelCamRelation *cChannelCamRelations::AddEntry(tChannelID ChannelID)
02100 {
02101   cMutexLock MutexLock(&mutex);
02102   cChannelCamRelation *ccr = GetEntry(ChannelID);
02103   if (!ccr)
02104      Add(ccr = new cChannelCamRelation(ChannelID));
02105   return ccr;
02106 }
02107 
02108 void cChannelCamRelations::Reset(int CamSlotNumber)
02109 {
02110   cMutexLock MutexLock(&mutex);
02111   for (cChannelCamRelation *ccr = First(); ccr; ccr = Next(ccr)) {
02112       ccr->ClrChecked(CamSlotNumber);
02113       ccr->ClrDecrypt(CamSlotNumber);
02114       }
02115 }
02116 
02117 bool cChannelCamRelations::CamChecked(tChannelID ChannelID, int CamSlotNumber)
02118 {
02119   cMutexLock MutexLock(&mutex);
02120   cChannelCamRelation *ccr = GetEntry(ChannelID);
02121   return ccr ? ccr->CamChecked(CamSlotNumber) : false;
02122 }
02123 
02124 bool cChannelCamRelations::CamDecrypt(tChannelID ChannelID, int CamSlotNumber)
02125 {
02126   cMutexLock MutexLock(&mutex);
02127   cChannelCamRelation *ccr = GetEntry(ChannelID);
02128   return ccr ? ccr->CamDecrypt(CamSlotNumber) : false;
02129 }
02130 
02131 void cChannelCamRelations::SetChecked(tChannelID ChannelID, int CamSlotNumber)
02132 {
02133   cMutexLock MutexLock(&mutex);
02134   cChannelCamRelation *ccr = AddEntry(ChannelID);
02135   if (ccr)
02136      ccr->SetChecked(CamSlotNumber);
02137 }
02138 
02139 void cChannelCamRelations::SetDecrypt(tChannelID ChannelID, int CamSlotNumber)
02140 {
02141   cMutexLock MutexLock(&mutex);
02142   cChannelCamRelation *ccr = AddEntry(ChannelID);
02143   if (ccr)
02144      ccr->SetDecrypt(CamSlotNumber);
02145 }
02146 
02147 void cChannelCamRelations::ClrChecked(tChannelID ChannelID, int CamSlotNumber)
02148 {
02149   cMutexLock MutexLock(&mutex);
02150   cChannelCamRelation *ccr = GetEntry(ChannelID);
02151   if (ccr)
02152      ccr->ClrChecked(CamSlotNumber);
02153 }
02154 
02155 void cChannelCamRelations::ClrDecrypt(tChannelID ChannelID, int CamSlotNumber)
02156 {
02157   cMutexLock MutexLock(&mutex);
02158   cChannelCamRelation *ccr = GetEntry(ChannelID);
02159   if (ccr)
02160      ccr->ClrDecrypt(CamSlotNumber);
02161 }