vdr  2.4.0
ci.c
Go to the documentation of this file.
1 /*
2  * ci.c: Common Interface
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * $Id: ci.c 4.21 2018/03/19 16:37:03 kls Exp $
8  */
9 
10 #include "ci.h"
11 #include <ctype.h>
12 #include <linux/dvb/ca.h>
13 #include <malloc.h>
14 #include <netinet/in.h>
15 #include <poll.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <sys/ioctl.h>
19 #include <time.h>
20 #include <unistd.h>
21 #include "device.h"
22 #include "mtd.h"
23 #include "pat.h"
24 #include "receiver.h"
25 #include "remux.h"
26 #include "libsi/si.h"
27 #include "skins.h"
28 #include "tools.h"
29 
30 // Set these to 'true' for debug output:
31 static bool DumpTPDUDataTransfer = false;
32 static bool DebugProtocol = false;
33 static bool DumpPolls = false;
34 static bool DumpDateTime = false;
35 
36 #define dbgprotocol(a...) do { if (DebugProtocol) fprintf(stderr, a); } while (0)
37 
38 // --- Helper functions ------------------------------------------------------
39 
40 #define SIZE_INDICATOR 0x80
41 
42 static const uint8_t *GetLength(const uint8_t *Data, int &Length)
46 {
47  Length = *Data++;
48  if ((Length & SIZE_INDICATOR) != 0) {
49  int l = Length & ~SIZE_INDICATOR;
50  Length = 0;
51  for (int i = 0; i < l; i++)
52  Length = (Length << 8) | *Data++;
53  }
54  return Data;
55 }
56 
57 static uint8_t *SetLength(uint8_t *Data, int Length)
60 {
61  uint8_t *p = Data;
62  if (Length < 128)
63  *p++ = Length;
64  else {
65  int n = sizeof(Length);
66  for (int i = n - 1; i >= 0; i--) {
67  int b = (Length >> (8 * i)) & 0xFF;
68  if (p != Data || b)
69  *++p = b;
70  }
71  *Data = (p - Data) | SIZE_INDICATOR;
72  p++;
73  }
74  return p;
75 }
76 
77 static char *CopyString(int Length, const uint8_t *Data)
80 {
81  char *s = MALLOC(char, Length + 1);
82  char *p = s;
83  while (Length > 0) {
84  char c = *Data;
85  if (isprint(c)) // some CAMs send funny characters in their strings, let's just skip them
86  *p++ = c;
87  else if (c == 0x8A) // the character 0x8A is used as newline, so let's put a real '\n' in there
88  *p++ = '\n';
89  Length--;
90  Data++;
91  }
92  *p = 0;
93  return s;
94 }
95 
96 static char *GetString(int &Length, const uint8_t **Data)
100 {
101  if (Length > 0 && Data && *Data) {
102  int l = 0;
103  const uint8_t *d = GetLength(*Data, l);
104  char *s = CopyString(l, d);
105  Length -= d - *Data + l;
106  *Data = d + l;
107  return s;
108  }
109  return NULL;
110 }
111 
112 // --- cCaPidReceiver --------------------------------------------------------
113 
114 // A receiver that is used to make the device receive the ECM pids, as well as the
115 // CAT and the EMM pids.
116 
117 class cCaPidReceiver : public cReceiver {
118 private:
121  uchar buffer[2048]; // 11 bit length, max. 2048 byte
123  uchar mtdCatBuffer[TS_SIZE]; // TODO: handle multi packet CATs!
124  int length;
127  void AddEmmPid(int Pid);
128  void DelEmmPids(void);
129 public:
130  cCaPidReceiver(void);
131  virtual ~cCaPidReceiver() { Detach(); }
132  virtual void Receive(const uchar *Data, int Length);
133  bool HasCaPids(void) const { return NumPids() - emmPids.Size() - 1 > 0; }
134  void Reset(void) { DelEmmPids(); catVersion = -1; }
135  bool HandlingPid(void);
144  };
145 
147 {
148  catVersion = -1;
149  bufp = NULL;
150  length = 0;
151  handlingPid = false;
152  cMutexLock MutexLock(&mutex);
153  handlingPid = true;
154  AddPid(CATPID);
155  handlingPid = false;
156 }
157 
159 {
160  for (int i = 0; i < emmPids.Size(); i++) {
161  if (emmPids[i] == Pid)
162  return;
163  }
164  emmPids.Append(Pid);
165  cMutexLock MutexLock(&mutex);
166  handlingPid = true;
167  AddPid(Pid);
168  handlingPid = false;
169 }
170 
172 {
173  cMutexLock MutexLock(&mutex);
174  handlingPid = true;
175  for (int i = 0; i < emmPids.Size(); i++)
176  DelPid(emmPids[i]);
177  emmPids.Clear();
178  handlingPid = false;
179 }
180 
181 void cCaPidReceiver::Receive(const uchar *Data, int Length)
182 {
183  if (TsPid(Data) == CATPID) {
184  cMtdCamSlot *MtdCamSlot = dynamic_cast<cMtdCamSlot *>(Device()->CamSlot());
185  const uchar *p = NULL;
186  if (TsPayloadStart(Data)) {
187  if (Data[5] == SI::TableIdCAT) {
188  length = (int(Data[6] & 0x03) << 8) | Data[7]; // section length
189  if (length > 5) {
190  int v = (Data[10] & 0x3E) >> 1; // version number
191  if (v != catVersion) {
192  if (Data[11] == 0 && Data[12] == 0) { // section number, last section number
193  if (length > TS_SIZE - 8) {
194  if (MtdCamSlot)
195  esyslog("ERROR: need to implement multi packet CAT handling for MTD!");
196  int n = TS_SIZE - 13;
197  memcpy(buffer, Data + 13, n);
198  bufp = buffer + n;
199  length -= n + 5; // 5 = header
200  }
201  else {
202  p = Data + 13; // no need to copy the data
203  length -= 5; // header
204  }
205  }
206  else
207  dsyslog("multi table CAT section - unhandled!");
208  catVersion = v;
209  }
210  else if (MtdCamSlot)
211  MtdCamSlot->PutCat(mtdCatBuffer, TS_SIZE);
212  }
213  }
214  }
215  else if (bufp && length > 0) {
216  int n = min(length, TS_SIZE - 4);
217  if (bufp + n - buffer <= int(sizeof(buffer))) {
218  memcpy(bufp, Data + 4, n);
219  bufp += n;
220  length -= n;
221  if (length <= 0) {
222  p = buffer;
223  length = bufp - buffer;
224  }
225  }
226  else {
227  esyslog("ERROR: buffer overflow in cCaPidReceiver::Receive()");
228  bufp = 0;
229  length = 0;
230  }
231  }
232  if (p) {
233  DelEmmPids();
234  for (int i = 0; i < length - 4; i++) { // -4 = checksum
235  if (p[i] == 0x09) {
236  int CaId = int(p[i + 2] << 8) | p[i + 3];
237  int EmmPid = Peek13(p + i + 4);
238  AddEmmPid(EmmPid);
239  if (MtdCamSlot)
240  MtdMapPid(const_cast<uchar *>(p + i + 4), MtdCamSlot->MtdMapper());
241  switch (CaId >> 8) {
242  case 0x01: for (int j = i + 7; j < p[i + 1] + 2; j += 4) {
243  EmmPid = Peek13(p + j);
244  AddEmmPid(EmmPid);
245  if (MtdCamSlot)
246  MtdMapPid(const_cast<uchar *>(p + j), MtdCamSlot->MtdMapper());
247  }
248  break;
249  }
250  i += p[i + 1] + 2 - 1; // -1 to compensate for the loop increment
251  }
252  }
253  p = NULL;
254  bufp = 0;
255  length = 0;
256  memcpy(mtdCatBuffer, Data, TS_SIZE);
257  if (MtdCamSlot)
258  MtdCamSlot->PutCat(mtdCatBuffer, TS_SIZE);
259  }
260  }
261 }
262 
264 {
265  cMutexLock MutexLock(&mutex);
266  return handlingPid;
267 }
268 
269 // --- cCaActivationReceiver -------------------------------------------------
270 
271 // A receiver that is used to make the device stay on a given channel and
272 // keep the CAM slot assigned.
273 
274 #define UNSCRAMBLE_TIME 5 // seconds of receiving purely unscrambled data before considering the smart card "activated"
275 #define TS_PACKET_FACTOR 1024 // only process every TS_PACKET_FACTORth packet to keep the load down
276 
278 private:
282 protected:
283  virtual void Receive(const uchar *Data, int Length);
284 public:
285  cCaActivationReceiver(const cChannel *Channel, cCamSlot *CamSlot);
286  virtual ~cCaActivationReceiver();
287  };
288 
290 :cReceiver(Channel, MINPRIORITY + 1)
291 {
292  camSlot = CamSlot;
293  lastScrambledTime = time(NULL);
294  numTsPackets = 0;
295 }
296 
298 {
299  Detach();
300 }
301 
302 void cCaActivationReceiver::Receive(const uchar *Data, int Length)
303 {
304  if (numTsPackets++ % TS_PACKET_FACTOR == 0) {
305  time_t Now = time(NULL);
306  if (TsIsScrambled(Data))
307  lastScrambledTime = Now;
308  else if (Now - lastScrambledTime > UNSCRAMBLE_TIME) {
309  dsyslog("CAM %d: activated!", camSlot->MasterSlotNumber());
310  Skins.QueueMessage(mtInfo, tr("CAM activated!"));
311  cDevice *d = Device();
312  Detach();
313  if (d) {
314  if (cCamSlot *s = d->CamSlot())
315  s->CancelActivation(); // this will delete *this* object, so no more code referencing *this* after this call!
316  }
317  }
318  }
319 }
320 
321 // --- cCamResponse ----------------------------------------------------------
322 
323 // CAM Response Actions:
324 
325 #define CRA_NONE 0
326 #define CRA_DISCARD -1
327 #define CRA_CONFIRM -2
328 #define CRA_SELECT -3
329 
330 class cCamResponse : public cListObject {
331 private:
333  char *text;
334  int action;
335 public:
336  cCamResponse(void);
337  ~cCamResponse();
338  bool Parse(const char *s);
339  int Matches(int CamNumber, const char *Text) const;
340  };
341 
343 {
344  camNumber = -1;
345  text = NULL;
346  action = CRA_NONE;
347 }
348 
350 {
351  free(text);
352 }
353 
354 bool cCamResponse::Parse(const char *s)
355 {
356  // Number:
357  s = skipspace(s);
358  if (*s == '*') {
359  camNumber = 0; // all CAMs
360  s++;
361  }
362  else {
363  char *e;
364  camNumber = strtol(s, &e, 10);
365  if (e == s || camNumber <= 0)
366  return false;
367  s = e;
368  }
369  // Text:
370  s = skipspace(s);
371  char *t = const_cast<char *>(s); // might have to modify it
372  char *q = NULL; // holds a copy in case of backslashes
373  bool InQuotes = false;
374  while (*t) {
375  if (*t == '"') {
376  if (t == s) { // opening quotes
377  InQuotes = true;
378  s++;
379  }
380  else if (InQuotes) // closing quotes
381  break;
382  }
383  else if (*t == '\\') {
384  if (!q) { // need to make a copy in order to strip backslashes
385  q = strdup(s);
386  t = q + (t - s);
387  s = q;
388  }
389  memmove(t, t + 1, strlen(t));
390  }
391  else if (*t == ' ') {
392  if (!InQuotes)
393  break;
394  }
395  t++;
396  }
397  free(text); // just for safety
398  text = NULL;
399  if (t != s) {
400  text = strndup(s, t - s);
401  s = t + 1;
402  }
403  free(q);
404  if (!text)
405  return false;
406  // Action:
407  s = skipspace(s);
408  if (strcasecmp(s, "DISCARD") == 0) action = CRA_DISCARD;
409  else if (strcasecmp(s, "CONFIRM") == 0) action = CRA_CONFIRM;
410  else if (strcasecmp(s, "SELECT") == 0) action = CRA_SELECT;
411  else if (isnumber(s)) action = atoi(s);
412  else
413  return false;
414  return true;
415 }
416 
417 int cCamResponse::Matches(int CamNumber, const char *Text) const
418 {
419  if (!camNumber || camNumber == CamNumber) {
420  if (strcmp(text, Text) == 0)
421  return action;
422  }
423  return CRA_NONE;
424 }
425 
426 // --- cCamResponses --------------------------------------------------------
427 
428 class cCamResponses : public cConfig<cCamResponse> {
429 public:
430  int GetMatch(int CamNumber, const char *Text) const;
431  };
432 
433 int cCamResponses::GetMatch(int CamNumber, const char *Text) const
434 {
435  for (const cCamResponse *cr = First(); cr; cr = Next(cr)) {
436  int Action = cr->Matches(CamNumber, Text);
437  if (Action != CRA_NONE) {
438  dsyslog("CAM %d: auto response %4d to '%s'\n", CamNumber, Action, Text);
439  return Action;
440  }
441  }
442  return CRA_NONE;
443 }
444 
446 
447 bool CamResponsesLoad(const char *FileName, bool AllowComments, bool MustExist)
448 {
449  return CamResponses.Load(FileName, AllowComments, MustExist);
450 }
451 
452 // --- cTPDU -----------------------------------------------------------------
453 
454 #define MAX_TPDU_SIZE 4096
455 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
456 
457 #define DATA_INDICATOR 0x80
458 
459 #define T_SB 0x80
460 #define T_RCV 0x81
461 #define T_CREATE_TC 0x82
462 #define T_CTC_REPLY 0x83
463 #define T_DELETE_TC 0x84
464 #define T_DTC_REPLY 0x85
465 #define T_REQUEST_TC 0x86
466 #define T_NEW_TC 0x87
467 #define T_TC_ERROR 0x88
468 #define T_DATA_LAST 0xA0
469 #define T_DATA_MORE 0xA1
470 
471 class cTPDU {
472 private:
473  int size;
475  const uint8_t *GetData(const uint8_t *Data, int &Length);
476 public:
477  cTPDU(void) { size = 0; }
478  cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length = 0, const uint8_t *Data = NULL);
479  uint8_t Slot(void) { return buffer[0]; }
480  uint8_t Tcid(void) { return buffer[1]; }
481  uint8_t Tag(void) { return buffer[2]; }
482  const uint8_t *Data(int &Length) { return GetData(buffer + 3, Length); }
483  uint8_t Status(void);
484  uint8_t *Buffer(void) { return buffer; }
485  int Size(void) { return size; }
486  void SetSize(int Size) { size = Size; }
487  int MaxSize(void) { return sizeof(buffer); }
488  void Dump(int SlotNumber, bool Outgoing);
489  };
490 
491 cTPDU::cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length, const uint8_t *Data)
492 {
493  size = 0;
494  buffer[0] = Slot;
495  buffer[1] = Tcid;
496  buffer[2] = Tag;
497  switch (Tag) {
498  case T_RCV:
499  case T_CREATE_TC:
500  case T_CTC_REPLY:
501  case T_DELETE_TC:
502  case T_DTC_REPLY:
503  case T_REQUEST_TC:
504  buffer[3] = 1; // length
505  buffer[4] = Tcid;
506  size = 5;
507  break;
508  case T_NEW_TC:
509  case T_TC_ERROR:
510  if (Length == 1) {
511  buffer[3] = 2; // length
512  buffer[4] = Tcid;
513  buffer[5] = Data[0];
514  size = 6;
515  }
516  else
517  esyslog("ERROR: invalid data length for TPDU tag 0x%02X: %d (%d/%d)", Tag, Length, Slot, Tcid);
518  break;
519  case T_DATA_LAST:
520  case T_DATA_MORE:
521  if (Length <= MAX_TPDU_DATA) {
522  uint8_t *p = buffer + 3;
523  p = SetLength(p, Length + 1);
524  *p++ = Tcid;
525  if (Length)
526  memcpy(p, Data, Length);
527  size = Length + (p - buffer);
528  }
529  else
530  esyslog("ERROR: invalid data length for TPDU tag 0x%02X: %d (%d/%d)", Tag, Length, Slot, Tcid);
531  break;
532  default:
533  esyslog("ERROR: unknown TPDU tag: 0x%02X (%d/%d)", Tag, Slot, Tcid);
534  }
535  }
536 
537 void cTPDU::Dump(int SlotNumber, bool Outgoing)
538 {
539  if (DumpTPDUDataTransfer && (DumpPolls || Tag() != T_SB)) {
540 #define MAX_DUMP 256
541  fprintf(stderr, " %d: %s ", SlotNumber, Outgoing ? "-->" : "<--");
542  for (int i = 0; i < size && i < MAX_DUMP; i++)
543  fprintf(stderr, "%02X ", buffer[i]);
544  fprintf(stderr, "%s\n", size >= MAX_DUMP ? "..." : "");
545  if (!Outgoing) {
546  fprintf(stderr, " ");
547  for (int i = 0; i < size && i < MAX_DUMP; i++)
548  fprintf(stderr, "%2c ", isprint(buffer[i]) ? buffer[i] : '.');
549  fprintf(stderr, "%s\n", size >= MAX_DUMP ? "..." : "");
550  }
551  }
552 }
553 
554 const uint8_t *cTPDU::GetData(const uint8_t *Data, int &Length)
555 {
556  if (size) {
557  Data = GetLength(Data, Length);
558  if (Length) {
559  Length--; // the first byte is always the tcid
560  return Data + 1;
561  }
562  }
563  return NULL;
564 }
565 
566 uint8_t cTPDU::Status(void)
567 {
568  if (size >= 4 && buffer[size - 4] == T_SB && buffer[size - 3] == 2)
569  return buffer[size - 1];
570  return 0;
571 }
572 
573 // --- cCiTransportConnection ------------------------------------------------
574 
575 #define MAX_SESSIONS_PER_TC 16
576 
578 private:
581  uint8_t tcid;
585  bool hasUserIO;
588  cCiSession *sessions[MAX_SESSIONS_PER_TC + 1]; // session numbering starts with 1
590  void SendTPDU(uint8_t Tag, int Length = 0, const uint8_t *Data = NULL);
591  void SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId = 0, int Status = -1);
592  void Poll(void);
593  uint32_t ResourceIdToInt(const uint8_t *Data);
594  cCiSession *GetSessionBySessionId(uint16_t SessionId);
595  void OpenSession(int Length, const uint8_t *Data);
596  void CloseSession(uint16_t SessionId);
597  void HandleSessions(cTPDU *TPDU);
598 public:
600  virtual ~cCiTransportConnection();
601  void SetTsPostProcessor(cCiSession *CiSession);
602  bool TsPostProcess(uint8_t *TsPacket);
603  cCamSlot *CamSlot(void) { return camSlot; }
604  uint8_t Tcid(void) const { return tcid; }
607  const char *GetCamName(void);
608  bool Ready(void);
609  bool HasUserIO(void) { return hasUserIO; }
610  void SendData(int Length, const uint8_t *Data);
611  bool Process(cTPDU *TPDU = NULL);
612  cCiSession *GetSessionByResourceId(uint32_t ResourceId);
613  };
614 
615 // --- cCiSession ------------------------------------------------------------
616 
617 // Session Tags:
618 
619 #define ST_SESSION_NUMBER 0x90
620 #define ST_OPEN_SESSION_REQUEST 0x91
621 #define ST_OPEN_SESSION_RESPONSE 0x92
622 #define ST_CREATE_SESSION 0x93
623 #define ST_CREATE_SESSION_RESPONSE 0x94
624 #define ST_CLOSE_SESSION_REQUEST 0x95
625 #define ST_CLOSE_SESSION_RESPONSE 0x96
626 
627 // Session Status:
628 
629 #define SS_OK 0x00
630 #define SS_NOT_ALLOCATED 0xF0
631 
632 // Resource Identifiers:
633 
634 #define RI_RESOURCE_MANAGER 0x00010041
635 #define RI_APPLICATION_INFORMATION 0x00020041
636 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
637 #define RI_HOST_CONTROL 0x00200041
638 #define RI_DATE_TIME 0x00240041
639 #define RI_MMI 0x00400041
640 
641 // Application Object Tags:
642 
643 #define AOT_NONE 0x000000
644 #define AOT_PROFILE_ENQ 0x9F8010
645 #define AOT_PROFILE 0x9F8011
646 #define AOT_PROFILE_CHANGE 0x9F8012
647 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
648 #define AOT_APPLICATION_INFO 0x9F8021
649 #define AOT_ENTER_MENU 0x9F8022
650 #define AOT_CA_INFO_ENQ 0x9F8030
651 #define AOT_CA_INFO 0x9F8031
652 #define AOT_CA_PMT 0x9F8032
653 #define AOT_CA_PMT_REPLY 0x9F8033
654 #define AOT_TUNE 0x9F8400
655 #define AOT_REPLACE 0x9F8401
656 #define AOT_CLEAR_REPLACE 0x9F8402
657 #define AOT_ASK_RELEASE 0x9F8403
658 #define AOT_DATE_TIME_ENQ 0x9F8440
659 #define AOT_DATE_TIME 0x9F8441
660 #define AOT_CLOSE_MMI 0x9F8800
661 #define AOT_DISPLAY_CONTROL 0x9F8801
662 #define AOT_DISPLAY_REPLY 0x9F8802
663 #define AOT_TEXT_LAST 0x9F8803
664 #define AOT_TEXT_MORE 0x9F8804
665 #define AOT_KEYPAD_CONTROL 0x9F8805
666 #define AOT_KEYPRESS 0x9F8806
667 #define AOT_ENQ 0x9F8807
668 #define AOT_ANSW 0x9F8808
669 #define AOT_MENU_LAST 0x9F8809
670 #define AOT_MENU_MORE 0x9F880A
671 #define AOT_MENU_ANSW 0x9F880B
672 #define AOT_LIST_LAST 0x9F880C
673 #define AOT_LIST_MORE 0x9F880D
674 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
675 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
676 #define AOT_DISPLAY_MESSAGE 0x9F8810
677 #define AOT_SCENE_END_MARK 0x9F8811
678 #define AOT_SCENE_DONE 0x9F8812
679 #define AOT_SCENE_CONTROL 0x9F8813
680 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
681 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
682 #define AOT_FLUSH_DOWNLOAD 0x9F8816
683 #define AOT_DOWNLOAD_REPLY 0x9F8817
684 #define AOT_COMMS_CMD 0x9F8C00
685 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
686 #define AOT_COMMS_REPLY 0x9F8C02
687 #define AOT_COMMS_SEND_LAST 0x9F8C03
688 #define AOT_COMMS_SEND_MORE 0x9F8C04
689 #define AOT_COMMS_RCV_LAST 0x9F8C05
690 #define AOT_COMMS_RCV_MORE 0x9F8C06
691 
692 #define RESOURCE_CLASS_MASK 0xFFFF0000
693 
694 cCiSession::cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc)
695 {
698  tc = Tc;
699 }
700 
702 {
703 }
704 
705 void cCiSession::SetResourceId(uint32_t Id)
706 {
707  resourceId = Id;
708 }
709 
711 {
712  tc->SetTsPostProcessor(this);
713 }
714 
715 int cCiSession::GetTag(int &Length, const uint8_t **Data)
719 {
720  if (Length >= 3 && Data && *Data) {
721  int t = 0;
722  for (int i = 0; i < 3; i++)
723  t = (t << 8) | *(*Data)++;
724  Length -= 3;
725  return t;
726  }
727  return AOT_NONE;
728 }
729 
730 const uint8_t *cCiSession::GetData(const uint8_t *Data, int &Length)
731 {
732  Data = GetLength(Data, Length);
733  return Length ? Data : NULL;
734 }
735 
736 void cCiSession::SendData(int Tag, int Length, const uint8_t *Data)
737 {
738  uint8_t buffer[MAX_TPDU_SIZE];
739  uint8_t *p = buffer;
740  *p++ = ST_SESSION_NUMBER;
741  *p++ = 0x02;
742  *p++ = (sessionId >> 8) & 0xFF;
743  *p++ = sessionId & 0xFF;
744  *p++ = (Tag >> 16) & 0xFF;
745  *p++ = (Tag >> 8) & 0xFF;
746  *p++ = Tag & 0xFF;
747  p = SetLength(p, Length);
748  if (p - buffer + Length < int(sizeof(buffer))) {
749  if (Data)
750  memcpy(p, Data, Length);
751  p += Length;
752  tc->SendData(p - buffer, buffer);
753  }
754  else
755  esyslog("ERROR: CAM %d: data length (%d) exceeds buffer size", CamSlot()->SlotNumber(), Length);
756 }
757 
759 {
760  return Tc()->CamSlot();
761 }
762 
763 void cCiSession::Process(int Length, const uint8_t *Data)
764 {
765 }
766 
767 // --- cCiResourceManager ----------------------------------------------------
768 
770 private:
771  int state;
772 public:
774  virtual void Process(int Length = 0, const uint8_t *Data = NULL);
775  };
776 
778 :cCiSession(SessionId, RI_RESOURCE_MANAGER, Tc)
779 {
780  dbgprotocol("Slot %d: new Resource Manager (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
781  state = 0;
782 }
783 
784 void cCiResourceManager::Process(int Length, const uint8_t *Data)
785 {
786  if (Data) {
787  int Tag = GetTag(Length, &Data);
788  switch (Tag) {
789  case AOT_PROFILE_ENQ: {
790  dbgprotocol("Slot %d: <== Profile Enquiry (%d)\n", CamSlot()->SlotNumber(), SessionId());
791  dbgprotocol("Slot %d: ==> Profile (%d)\n", CamSlot()->SlotNumber(), SessionId());
792  SendData(AOT_PROFILE, CiResourceHandlers.NumIds() * sizeof(uint32_t), (uint8_t*)CiResourceHandlers.Ids());
793  state = 3;
794  }
795  break;
796  case AOT_PROFILE: {
797  dbgprotocol("Slot %d: <== Profile (%d)\n", CamSlot()->SlotNumber(), SessionId());
798  if (state == 1) {
799  int l = 0;
800  const uint8_t *d = GetData(Data, l);
801  if (l > 0 && d)
802  esyslog("ERROR: CAM %d: resource manager: unexpected data", CamSlot()->SlotNumber());
803  dbgprotocol("Slot %d: ==> Profile Change (%d)\n", CamSlot()->SlotNumber(), SessionId());
805  state = 2;
806  }
807  else {
808  esyslog("ERROR: CAM %d: resource manager: unexpected tag %06X in state %d", CamSlot()->SlotNumber(), Tag, state);
809  }
810  }
811  break;
812  default: esyslog("ERROR: CAM %d: resource manager: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
813  }
814  }
815  else if (state == 0) {
816  dbgprotocol("Slot %d: ==> Profile Enq (%d)\n", CamSlot()->SlotNumber(), SessionId());
818  state = 1;
819  }
820 }
821 
822 // --- cCiApplicationInformation ---------------------------------------------
823 
825 :cCiSession(SessionId, RI_APPLICATION_INFORMATION, Tc)
826 {
827  dbgprotocol("Slot %d: new Application Information (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
828  state = 0;
829  menuString = NULL;
830 }
831 
833 {
834  free(menuString);
835 }
836 
837 void cCiApplicationInformation::Process(int Length, const uint8_t *Data)
838 {
839  if (Data) {
840  int Tag = GetTag(Length, &Data);
841  switch (Tag) {
842  case AOT_APPLICATION_INFO: {
843  dbgprotocol("Slot %d: <== Application Info (%d)\n", CamSlot()->SlotNumber(), SessionId());
844  int l = 0;
845  const uint8_t *d = GetData(Data, l);
846  if ((l -= 1) < 0) break;
847  applicationType = *d++;
848  if ((l -= 2) < 0) break;
849  applicationManufacturer = ntohs(get_unaligned((uint16_t *)d));
850  d += 2;
851  if ((l -= 2) < 0) break;
852  manufacturerCode = ntohs(get_unaligned((uint16_t *)d));
853  d += 2;
854  free(menuString);
855  menuString = GetString(l, &d);
856  isyslog("CAM %d: %s, %02X, %04X, %04X", CamSlot()->SlotNumber(), menuString, applicationType, applicationManufacturer, manufacturerCode);
857  state = 2;
858  }
859  break;
860  default: esyslog("ERROR: CAM %d: application information: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
861  }
862  }
863  else if (state == 0) {
864  dbgprotocol("Slot %d: ==> Application Info Enq (%d)\n", CamSlot()->SlotNumber(), SessionId());
866  state = 1;
867  }
868 }
869 
871 {
872  if (state == 2) {
873  dbgprotocol("Slot %d: ==> Enter Menu (%d)\n", CamSlot()->SlotNumber(), SessionId());
875  return true;
876  }
877  return false;
878 }
879 
880 // --- cCiCaPmt --------------------------------------------------------------
881 
882 #define MAXCASYSTEMIDS 64
883 
884 // Ca Pmt List Management:
885 
886 #define CPLM_MORE 0x00
887 #define CPLM_FIRST 0x01
888 #define CPLM_LAST 0x02
889 #define CPLM_ONLY 0x03
890 #define CPLM_ADD 0x04
891 #define CPLM_UPDATE 0x05
892 
893 // Ca Pmt Cmd Ids:
894 
895 #define CPCI_OK_DESCRAMBLING 0x01
896 #define CPCI_OK_MMI 0x02
897 #define CPCI_QUERY 0x03
898 #define CPCI_NOT_SELECTED 0x04
899 
900 class cCiCaPmt {
902 private:
903  uint8_t cmdId;
907  int source;
910  int caSystemIds[MAXCASYSTEMIDS + 1]; // list is zero terminated!
911  void AddCaDescriptors(int Length, const uint8_t *Data);
912 public:
913  cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds);
914  uint8_t CmdId(void) { return cmdId; }
915  void SetListManagement(uint8_t ListManagement);
916  uint8_t ListManagement(void) { return capmt.Get(0); }
917  void AddPid(int Pid, uint8_t StreamType);
918  void MtdMapPids(cMtdMapper *MtdMapper);
919  };
920 
921 cCiCaPmt::cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds)
922 {
923  cmdId = CmdId;
924  source = Source;
925  transponder = Transponder;
926  programNumber = ProgramNumber;
927  int i = 0;
928  if (CaSystemIds) {
929  for (; CaSystemIds[i]; i++)
930  caSystemIds[i] = CaSystemIds[i];
931  }
932  caSystemIds[i] = 0;
935  capmt.Append((ProgramNumber >> 8) & 0xFF);
936  capmt.Append( ProgramNumber & 0xFF);
937  capmt.Append(0x01); // version_number, current_next_indicator - apparently vn doesn't matter, but cni must be 1
939  capmt.Append(0x00); // program_info_length H (at program level)
940  capmt.Append(0x00); // program_info_length L
942 }
943 
944 void cCiCaPmt::SetListManagement(uint8_t ListManagement)
945 {
947 }
948 
949 void cCiCaPmt::AddPid(int Pid, uint8_t StreamType)
950 {
951  if (Pid) {
953  capmt.Append(StreamType);
954  capmt.Append((Pid >> 8) & 0xFF);
955  capmt.Append( Pid & 0xFF);
957  capmt.Append(0x00); // ES_info_length H (at ES level)
958  capmt.Append(0x00); // ES_info_length L
960  }
961 }
962 
963 void cCiCaPmt::AddCaDescriptors(int Length, const uint8_t *Data)
964 {
965  if (esInfoLengthPos) {
966  if (Length || cmdId == CPCI_QUERY) {
967  capmt.Append(cmdId);
968  capmt.Append(Data, Length);
969  int l = capmt.Length() - esInfoLengthPos - 2;
970  capmt.Set(esInfoLengthPos, (l >> 8) & 0xFF);
971  capmt.Set(esInfoLengthPos + 1, l & 0xFF);
972  }
973  esInfoLengthPos = 0;
974  }
975  else
976  esyslog("ERROR: adding CA descriptor without Pid!");
977 }
978 
979 static int MtdMapCaDescriptor(uchar *p, cMtdMapper *MtdMapper)
980 {
981  // See pat.c: cCaDescriptor::cCaDescriptor() for the layout of the data!
982  if (*p == SI::CaDescriptorTag) {
983  int l = *++p;
984  if (l >= 4) {
985  MtdMapPid(p + 3, MtdMapper);
986  return l + 2;
987  }
988  else
989  esyslog("ERROR: wrong length (%d) in MtdMapCaDescriptor()", l);
990  }
991  else
992  esyslog("ERROR: wrong tag (%d) in MtdMapCaDescriptor()", *p);
993  return -1;
994 }
995 
996 static int MtdMapCaDescriptors(uchar *p, cMtdMapper *MtdMapper)
997 {
998  int Length = p[0] * 256 + p[1];
999  if (Length >= 3) {
1000  p += 3;
1001  int m = Length - 1;
1002  while (m > 0) {
1003  int l = MtdMapCaDescriptor(p, MtdMapper);
1004  if (l > 0) {
1005  p += l;
1006  m -= l;
1007  }
1008  }
1009  }
1010  return Length + 2;
1011 }
1012 
1013 static int MtdMapStream(uchar *p, cMtdMapper *MtdMapper)
1014 {
1015  // See ci.c: cCiCaPmt::AddPid() for the layout of the data!
1016  MtdMapPid(p + 1, MtdMapper);
1017  int l = MtdMapCaDescriptors(p + 3, MtdMapper);
1018  if (l > 0)
1019  return l + 3;
1020  return -1;
1021 }
1022 
1023 static int MtdMapStreams(uchar *p, cMtdMapper *MtdMapper, int Length)
1024 {
1025  int m = Length;
1026  while (m >= 5) {
1027  int l = MtdMapStream(p, MtdMapper);
1028  if (l > 0) {
1029  p += l;
1030  m -= l;
1031  }
1032  else
1033  break;
1034  }
1035  return Length;
1036 }
1037 
1039 {
1040  uchar *p = capmt.Data();
1041  int m = capmt.Length();
1042  if (m >= 3) {
1043  MtdMapSid(p + 1, MtdMapper);
1044  p += 4;
1045  m -= 4;
1046  if (m >= 2) {
1047  int l = MtdMapCaDescriptors(p, MtdMapper);
1048  if (l >= 0) {
1049  p += l;
1050  m -= l;
1051  MtdMapStreams(p, MtdMapper, m);
1052  }
1053  }
1054  }
1055 }
1056 
1057 // --- cCiConditionalAccessSupport -------------------------------------------
1058 
1059 // CA Enable Ids:
1060 
1061 #define CAEI_POSSIBLE 0x01
1062 #define CAEI_POSSIBLE_COND_PURCHASE 0x02
1063 #define CAEI_POSSIBLE_COND_TECHNICAL 0x03
1064 #define CAEI_NOT_POSSIBLE_ENTITLEMENT 0x71
1065 #define CAEI_NOT_POSSIBLE_TECHNICAL 0x73
1066 
1067 #define CA_ENABLE_FLAG 0x80
1068 
1069 #define CA_ENABLE(x) (((x) & CA_ENABLE_FLAG) ? (x) & ~CA_ENABLE_FLAG : 0)
1070 
1071 #define QUERY_WAIT_TIME 500 // ms to wait before sending a query
1072 #define QUERY_REPLY_TIMEOUT 2000 // ms to wait for a reply to a query
1073 #define QUERY_RETRIES 6 // max. number of retries to check if there is a reply to a query
1074 
1076 private:
1077  int state;
1079  int caSystemIds[MAXCASYSTEMIDS + 1]; // list is zero terminated!
1083 public:
1085  virtual void Process(int Length = 0, const uint8_t *Data = NULL);
1086  const int *GetCaSystemIds(void) { return caSystemIds; }
1087  void SendPMT(cCiCaPmt *CaPmt);
1088  bool RepliesToQuery(void) { return repliesToQuery; }
1089  bool Ready(void) { return state >= 4; }
1090  bool ReceivedReply(void) { return state >= 5; }
1091  bool CanDecrypt(void) { return state == 6; }
1092  };
1093 
1096 {
1097  dbgprotocol("Slot %d: new Conditional Access Support (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
1098  state = 0; // inactive
1099  caSystemIds[numCaSystemIds = 0] = 0;
1100  repliesToQuery = false;
1101  numRetries = 0;
1102 }
1103 
1104 void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
1105 {
1106  if (Data) {
1107  int Tag = GetTag(Length, &Data);
1108  switch (Tag) {
1109  case AOT_CA_INFO: {
1110  dbgprotocol("Slot %d: <== Ca Info (%d)", CamSlot()->SlotNumber(), SessionId());
1111  cString Ids;
1112  numCaSystemIds = 0;
1113  int l = 0;
1114  const uint8_t *d = GetData(Data, l);
1115  while (l > 1) {
1116  uint16_t id = ((uint16_t)(*d) << 8) | *(d + 1);
1117  Ids = cString::sprintf("%s %04X", *Ids ? *Ids : "", id);
1118  dbgprotocol(" %04X", id);
1119  d += 2;
1120  l -= 2;
1122  caSystemIds[numCaSystemIds++] = id;
1123  else {
1124  esyslog("ERROR: CAM %d: too many CA system IDs!", CamSlot()->SlotNumber());
1125  break;
1126  }
1127  }
1129  dbgprotocol("\n");
1130  if (state == 1) {
1131  timer.Set(0);
1133  state = 2; // got ca info
1134  }
1135  dsyslog("CAM %d: system ids:%s", CamSlot()->SlotNumber(), *Ids ? *Ids : " none");
1136  }
1137  break;
1138  case AOT_CA_PMT_REPLY: {
1139  dbgprotocol("Slot %d: <== Ca Pmt Reply (%d)", CamSlot()->SlotNumber(), SessionId());
1140  if (!repliesToQuery) {
1141  if (CamSlot()->IsMasterSlot())
1142  dsyslog("CAM %d: replies to QUERY - multi channel decryption (MCD) possible", CamSlot()->SlotNumber());
1143  repliesToQuery = true;
1144  if (CamSlot()->MtdAvailable()) {
1145  if (CamSlot()->IsMasterSlot())
1146  dsyslog("CAM %d: supports multi transponder decryption (MTD)", CamSlot()->SlotNumber());
1147  CamSlot()->MtdActivate(true);
1148  }
1149  }
1150  state = 5; // got ca pmt reply
1151  int l = 0;
1152  const uint8_t *d = GetData(Data, l);
1153  if (l > 1) {
1154  uint16_t pnr = ((uint16_t)(*d) << 8) | *(d + 1);
1155  dbgprotocol(" %d", pnr);
1156  d += 2;
1157  l -= 2;
1158  if (l > 0) {
1159  dbgprotocol(" %02X", *d);
1160  d += 1;
1161  l -= 1;
1162  if (l > 0) {
1163  if (l % 3 == 0 && l > 1) {
1164  // The EN50221 standard defines that the next byte is supposed
1165  // to be the CA_enable value at programme level. However, there are
1166  // CAMs (for instance the AlphaCrypt with firmware <= 3.05) that
1167  // insert a two byte length field here.
1168  // This is a workaround to skip this length field:
1169  uint16_t len = ((uint16_t)(*d) << 8) | *(d + 1);
1170  if (len == l - 2) {
1171  d += 2;
1172  l -= 2;
1173  }
1174  }
1175  unsigned char caepl = *d;
1176  dbgprotocol(" %02X", caepl);
1177  d += 1;
1178  l -= 1;
1179  bool ok = true;
1180  if (l <= 2)
1181  ok = CA_ENABLE(caepl) == CAEI_POSSIBLE;
1182  while (l > 2) {
1183  uint16_t pid = ((uint16_t)(*d) << 8) | *(d + 1);
1184  unsigned char caees = *(d + 2);
1185  dbgprotocol(" %d=%02X", pid, caees);
1186  d += 3;
1187  l -= 3;
1188  if (CA_ENABLE(caees) != CAEI_POSSIBLE)
1189  ok = false;
1190  }
1191  if (ok)
1192  state = 6; // descrambling possible
1193  }
1194  }
1195  }
1196  dbgprotocol("\n");
1197  }
1198  break;
1199  default: esyslog("ERROR: CAM %d: conditional access support: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
1200  }
1201  }
1202  else if (state == 0) {
1203  dbgprotocol("Slot %d: ==> Ca Info Enq (%d)\n", CamSlot()->SlotNumber(), SessionId());
1205  state = 1; // enquired ca info
1206  }
1207  else if ((state == 2 || state == 3) && timer.TimedOut()) {
1208  if (numRetries-- > 0) {
1209  cCiCaPmt CaPmt(CPCI_QUERY, 0, 0, 0, NULL);
1210  SendPMT(&CaPmt);
1212  state = 3; // waiting for reply
1213  }
1214  else {
1215  dsyslog("CAM %d: doesn't reply to QUERY - only a single channel can be decrypted", CamSlot()->SlotNumber());
1216  state = 4; // normal operation
1217  }
1218  }
1219 }
1220 
1222 {
1223  if (CaPmt && state >= 2) {
1224  dbgprotocol("Slot %d: ==> Ca Pmt (%d) %d %d\n", CamSlot()->SlotNumber(), SessionId(), CaPmt->ListManagement(), CaPmt->CmdId());
1225  SendData(AOT_CA_PMT, CaPmt->capmt.Length(), CaPmt->capmt.Data());
1226  state = 4; // sent ca pmt
1227  }
1228 }
1229 
1230 // --- cCiHostControl --------------------------------------------------------
1231 
1232 class cCiHostControl : public cCiSession {
1233 public:
1235  virtual void Process(int Length = 0, const uint8_t *Data = NULL);
1236  };
1237 
1239 :cCiSession(SessionId, RI_HOST_CONTROL, Tc)
1240 {
1241  dbgprotocol("Slot %d: new Host Control (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
1242 }
1243 
1244 void cCiHostControl::Process(int Length, const uint8_t* Data)
1245 {
1246  if (Data) {
1247  int Tag = GetTag(Length, &Data);
1248  switch (Tag) {
1249  case AOT_TUNE:
1250  dbgprotocol("Slot %d: <== Host Control Tune (%d)\n", CamSlot()->SlotNumber(), SessionId());
1251  break;
1252  case AOT_REPLACE:
1253  dbgprotocol("Slot %d: <== Host Control Replace (%d)\n", CamSlot()->SlotNumber(), SessionId());
1254  break;
1255  case AOT_CLEAR_REPLACE:
1256  dbgprotocol("Slot %d: <== Host Control Clear Replace (%d)\n", CamSlot()->SlotNumber(), SessionId());
1257  break;
1258  default: esyslog("ERROR: CAM %d: Host Control: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
1259  }
1260  }
1261 }
1262 
1263 // --- cCiDateTime -----------------------------------------------------------
1264 
1265 class cCiDateTime : public cCiSession {
1266 private:
1268  time_t lastTime;
1269  void SendDateTime(void);
1270 public:
1272  virtual void Process(int Length = 0, const uint8_t *Data = NULL);
1273  };
1274 
1276 :cCiSession(SessionId, RI_DATE_TIME, Tc)
1277 {
1278  interval = 0;
1279  lastTime = 0;
1280  dbgprotocol("Slot %d: new Date Time (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
1281 }
1282 
1284 {
1285  time_t t = time(NULL);
1286  struct tm tm_gmt;
1287  struct tm tm_loc;
1288  if (gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc)) {
1289  int Y = tm_gmt.tm_year;
1290  int M = tm_gmt.tm_mon + 1;
1291  int D = tm_gmt.tm_mday;
1292  int L = (M == 1 || M == 2) ? 1 : 0;
1293  int MJD = 14956 + D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001);
1294 #define DEC2BCD(d) uint8_t(((d / 10) << 4) + (d % 10))
1295 #pragma pack(1)
1296  struct tTime { uint16_t mjd; uint8_t h, m, s; short offset; };
1297 #pragma pack()
1298  tTime T = { mjd : htons(MJD), h : DEC2BCD(tm_gmt.tm_hour), m : DEC2BCD(tm_gmt.tm_min), s : DEC2BCD(tm_gmt.tm_sec), offset : short(htons(tm_loc.tm_gmtoff / 60)) };
1299  bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer;
1301  if (DumpDateTime)
1302  dbgprotocol("Slot %d: ==> Date Time (%d)\n", CamSlot()->SlotNumber(), SessionId());
1303  SendData(AOT_DATE_TIME, 7, (uint8_t*)&T);
1304  DumpTPDUDataTransfer = OldDumpTPDUDataTransfer;
1305  }
1306 }
1307 
1308 void cCiDateTime::Process(int Length, const uint8_t *Data)
1309 {
1310  if (Data) {
1311  int Tag = GetTag(Length, &Data);
1312  switch (Tag) {
1313  case AOT_DATE_TIME_ENQ: {
1314  interval = 0;
1315  int l = 0;
1316  const uint8_t *d = GetData(Data, l);
1317  if (l > 0)
1318  interval = *d;
1319  dbgprotocol("Slot %d: <== Date Time Enq (%d), interval = %d\n", CamSlot()->SlotNumber(), SessionId(), interval);
1320  lastTime = time(NULL);
1321  SendDateTime();
1322  }
1323  break;
1324  default: esyslog("ERROR: CAM %d: date time: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
1325  }
1326  }
1327  else if (interval && time(NULL) - lastTime > interval) {
1328  lastTime = time(NULL);
1329  SendDateTime();
1330  }
1331 }
1332 
1333 // --- cCiMMI ----------------------------------------------------------------
1334 
1335 // Display Control Commands:
1336 
1337 #define DCC_SET_MMI_MODE 0x01
1338 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
1339 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
1340 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
1341 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
1342 
1343 // MMI Modes:
1344 
1345 #define MM_HIGH_LEVEL 0x01
1346 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
1347 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
1348 
1349 // Display Reply IDs:
1350 
1351 #define DRI_MMI_MODE_ACK 0x01
1352 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
1353 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
1354 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
1355 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
1356 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
1357 #define DRI_UNKNOWN_MMI_MODE 0xF1
1358 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
1359 
1360 // Enquiry Flags:
1361 
1362 #define EF_BLIND 0x01
1363 
1364 // Answer IDs:
1365 
1366 #define AI_CANCEL 0x00
1367 #define AI_ANSWER 0x01
1368 
1369 class cCiMMI : public cCiSession {
1370 private:
1371  char *GetText(int &Length, const uint8_t **Data);
1374 public:
1376  virtual ~cCiMMI();
1377  virtual void Process(int Length = 0, const uint8_t *Data = NULL);
1378  virtual bool HasUserIO(void) { return menu || enquiry; }
1379  cCiMenu *Menu(bool Clear = false);
1380  cCiEnquiry *Enquiry(bool Clear = false);
1381  void SendMenuAnswer(uint8_t Selection);
1382  bool SendAnswer(const char *Text);
1383  bool SendCloseMMI(void);
1384  };
1385 
1386 cCiMMI::cCiMMI(uint16_t SessionId, cCiTransportConnection *Tc)
1387 :cCiSession(SessionId, RI_MMI, Tc)
1388 {
1389  dbgprotocol("Slot %d: new MMI (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
1390  menu = fetchedMenu = NULL;
1391  enquiry = fetchedEnquiry = NULL;
1392 }
1393 
1395 {
1396  if (fetchedMenu) {
1397  cMutexLock MutexLock(fetchedMenu->mutex);
1398  fetchedMenu->mmi = NULL;
1399  }
1400  delete menu;
1401  if (fetchedEnquiry) {
1402  cMutexLock MutexLock(fetchedEnquiry->mutex);
1403  fetchedEnquiry->mmi = NULL;
1404  }
1405  delete enquiry;
1406 }
1407 
1408 char *cCiMMI::GetText(int &Length, const uint8_t **Data)
1412 {
1413  int Tag = GetTag(Length, Data);
1414  if (Tag == AOT_TEXT_LAST) {
1415  char *s = GetString(Length, Data);
1416  dbgprotocol("Slot %d: <== Text Last (%d) '%s'\n", CamSlot()->SlotNumber(), SessionId(), s);
1417  return s;
1418  }
1419  else
1420  esyslog("ERROR: CAM %d: MMI: unexpected text tag: %06X", CamSlot()->SlotNumber(), Tag);
1421  return NULL;
1422 }
1423 
1424 void cCiMMI::Process(int Length, const uint8_t *Data)
1425 {
1426  if (Data) {
1427  int Tag = GetTag(Length, &Data);
1428  switch (Tag) {
1429  case AOT_DISPLAY_CONTROL: {
1430  dbgprotocol("Slot %d: <== Display Control (%d)\n", CamSlot()->SlotNumber(), SessionId());
1431  int l = 0;
1432  const uint8_t *d = GetData(Data, l);
1433  if (l > 0) {
1434  switch (*d) {
1435  case DCC_SET_MMI_MODE:
1436  if (l == 2 && *++d == MM_HIGH_LEVEL) {
1437  struct tDisplayReply { uint8_t id; uint8_t mode; };
1438  tDisplayReply dr = { id : DRI_MMI_MODE_ACK, mode : MM_HIGH_LEVEL };
1439  dbgprotocol("Slot %d: ==> Display Reply (%d)\n", CamSlot()->SlotNumber(), SessionId());
1440  SendData(AOT_DISPLAY_REPLY, 2, (uint8_t *)&dr);
1441  }
1442  break;
1443  default: esyslog("ERROR: CAM %d: MMI: unsupported display control command %02X", CamSlot()->SlotNumber(), *d);
1444  }
1445  }
1446  }
1447  break;
1448  case AOT_LIST_LAST:
1449  case AOT_MENU_LAST: {
1450  dbgprotocol("Slot %d: <== Menu Last (%d)\n", CamSlot()->SlotNumber(), SessionId());
1451  delete menu;
1452  menu = new cCiMenu(this, Tag == AOT_MENU_LAST);
1453  int l = 0;
1454  const uint8_t *d = GetData(Data, l);
1455  if (l > 0) {
1456  // since the specification allows choiceNb to be undefined it is useless, so let's just skip it:
1457  d++;
1458  l--;
1459  if (l > 0) menu->titleText = GetText(l, &d);
1460  if (l > 0) menu->subTitleText = GetText(l, &d);
1461  if (l > 0) menu->bottomText = GetText(l, &d);
1462  int Action = CRA_NONE;
1463  int Select = -1;
1464  int Item = 0;
1465  while (l > 0) {
1466  char *s = GetText(l, &d);
1467  if (s) {
1468  if (!menu->AddEntry(s))
1469  free(s);
1470  else if (Action == CRA_NONE) {
1471  Action = CamResponses.GetMatch(CamSlot()->SlotNumber(), s);
1472  if (Action == CRA_SELECT)
1473  Select = Item;
1474  }
1475  }
1476  else
1477  break;
1478  Item++;
1479  }
1480  if (Action != CRA_NONE) {
1481  delete menu;
1482  menu = NULL;
1483  cCondWait::SleepMs(100);
1484  if (Action == CRA_DISCARD) {
1485  SendCloseMMI();
1486  dsyslog("CAM %d: DISCARD", CamSlot()->SlotNumber());
1487  }
1488  else if (Action == CRA_CONFIRM) {
1489  SendMenuAnswer(1);
1490  dsyslog("CAM %d: CONFIRM", CamSlot()->SlotNumber());
1491  }
1492  else if (Action == CRA_SELECT) {
1493  SendMenuAnswer(Select + 1);
1494  dsyslog("CAM %d: SELECT %d", CamSlot()->SlotNumber(), Select + 1);
1495  }
1496  }
1497  }
1498  }
1499  break;
1500  case AOT_ENQ: {
1501  dbgprotocol("Slot %d: <== Enq (%d)\n", CamSlot()->SlotNumber(), SessionId());
1502  delete enquiry;
1503  enquiry = new cCiEnquiry(this);
1504  int l = 0;
1505  const uint8_t *d = GetData(Data, l);
1506  if (l > 0) {
1507  uint8_t blind = *d++;
1508  //XXX GetByte()???
1509  l--;
1510  enquiry->blind = blind & EF_BLIND;
1511  enquiry->expectedLength = *d++;
1512  l--;
1513  // I really wonder why there is no text length field here...
1514  enquiry->text = CopyString(l, d);
1515  int Action = CamResponses.GetMatch(CamSlot()->SlotNumber(), enquiry->text);
1516  if (Action > CRA_NONE) {
1517  char s[enquiry->expectedLength * 2];
1518  snprintf(s, sizeof(s), "%d", Action);
1519  if (int(strlen(s)) == enquiry->expectedLength) {
1520  delete enquiry;
1521  enquiry = NULL;
1522  SendAnswer(s);
1523  dsyslog("CAM %d: PIN", CamSlot()->SlotNumber());
1524  }
1525  else
1526  esyslog("CAM %d: ERROR: unexpected PIN length %d, expected %d", CamSlot()->SlotNumber(), int(strlen(s)), enquiry->expectedLength);
1527  }
1528  }
1529  }
1530  break;
1531  case AOT_CLOSE_MMI: {
1532  int id = -1;
1533  int delay = -1;
1534  int l = 0;
1535  const uint8_t *d = GetData(Data, l);
1536  if (l > 0) {
1537  id = *d++;
1538  if (l > 1)
1539  delay = *d;
1540  }
1541  dbgprotocol("Slot %d: <== Close MMI (%d) id = %02X delay = %d\n", CamSlot()->SlotNumber(), SessionId(), id, delay);
1542  }
1543  break;
1544  default: esyslog("ERROR: CAM %d: MMI: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
1545  }
1546  }
1547 }
1548 
1549 cCiMenu *cCiMMI::Menu(bool Clear)
1550 {
1551  if (Clear)
1552  fetchedMenu = NULL;
1553  else if (menu) {
1554  fetchedMenu = menu;
1555  menu = NULL;
1556  }
1557  return fetchedMenu;
1558 }
1559 
1561 {
1562  if (Clear)
1563  fetchedEnquiry = NULL;
1564  else if (enquiry) {
1566  enquiry = NULL;
1567  }
1568  return fetchedEnquiry;
1569 }
1570 
1571 void cCiMMI::SendMenuAnswer(uint8_t Selection)
1572 {
1573  dbgprotocol("Slot %d: ==> Menu Answ (%d)\n", CamSlot()->SlotNumber(), SessionId());
1574  SendData(AOT_MENU_ANSW, 1, &Selection);
1575 }
1576 
1577 bool cCiMMI::SendAnswer(const char *Text)
1578 {
1579  dbgprotocol("Slot %d: ==> Answ (%d)\n", CamSlot()->SlotNumber(), SessionId());
1580  struct tAnswer { uint8_t id; char text[256]; };//XXX
1581  tAnswer answer;
1582  answer.id = Text ? AI_ANSWER : AI_CANCEL;
1583  if (Text)
1584  strncpy(answer.text, Text, sizeof(answer.text));
1585  SendData(AOT_ANSW, Text ? strlen(Text) + 1 : 1, (uint8_t *)&answer);
1586  return true;
1587 }
1588 
1590 {
1591  dbgprotocol("Slot %d: ==> Close MMI (%d)\n", CamSlot()->SlotNumber(), SessionId());
1592  SendData(AOT_CLOSE_MMI, 0);
1593  return true;
1594 }
1595 
1596 // --- cCiMenu ---------------------------------------------------------------
1597 
1598 cCiMenu::cCiMenu(cCiMMI *MMI, bool Selectable)
1599 {
1600  mmi = MMI;
1601  mutex = NULL;
1603  titleText = subTitleText = bottomText = NULL;
1604  numEntries = 0;
1605 }
1606 
1608 {
1609  cMutexLock MutexLock(mutex);
1610  if (mmi)
1611  mmi->Menu(true);
1612  free(titleText);
1613  free(subTitleText);
1614  free(bottomText);
1615  for (int i = 0; i < numEntries; i++)
1616  free(entries[i]);
1617 }
1618 
1619 bool cCiMenu::AddEntry(char *s)
1620 {
1622  entries[numEntries++] = s;
1623  return true;
1624  }
1625  return false;
1626 }
1627 
1629 {
1630  // If the mmi is gone, the menu shall be closed, which also qualifies as 'update'.
1631  return !mmi || mmi->HasUserIO();
1632 }
1633 
1634 void cCiMenu::Select(int Index)
1635 {
1636  cMutexLock MutexLock(mutex);
1637  if (mmi && -1 <= Index && Index < numEntries)
1638  mmi->SendMenuAnswer(Index + 1);
1639 }
1640 
1642 {
1643  Select(-1);
1644 }
1645 
1646 void cCiMenu::Abort(void)
1647 {
1648  cMutexLock MutexLock(mutex);
1649  if (mmi)
1650  mmi->SendCloseMMI();
1651 }
1652 
1653 // --- cCiEnquiry ------------------------------------------------------------
1654 
1656 {
1657  mmi = MMI;
1658  mutex = NULL;
1659  text = NULL;
1660  blind = false;
1661  expectedLength = 0;
1662 }
1663 
1665 {
1666  cMutexLock MutexLock(mutex);
1667  if (mmi)
1668  mmi->Enquiry(true);
1669  free(text);
1670 }
1671 
1672 void cCiEnquiry::Reply(const char *s)
1673 {
1674  cMutexLock MutexLock(mutex);
1675  if (mmi)
1676  mmi->SendAnswer(s);
1677 }
1678 
1680 {
1681  Reply(NULL);
1682 }
1683 
1685 {
1686  cMutexLock MutexLock(mutex);
1687  if (mmi)
1688  mmi->SendCloseMMI();
1689 }
1690 
1691 // --- cCiResourceHandler ----------------------------------------------------
1692 
1694 {
1695 }
1696 
1698 {
1699 }
1700 
1701 // --- cCiDefaultResourceHandler ---------------------------------------------
1702 
1704 public:
1705  virtual const uint32_t *ResourceIds(void) const;
1706  virtual cCiSession *GetNewCiSession(uint32_t ResourceId, uint16_t SessionId, cCiTransportConnection *Tc);
1707  };
1708 
1709 const uint32_t *cCiDefaultResourceHandler::ResourceIds(void) const
1710 {
1711  static uint32_t Ids[] = {
1716  RI_DATE_TIME,
1717  RI_MMI,
1718  0
1719  };
1720  return Ids;
1721 }
1722 
1724 {
1725  switch (ResourceId) {
1726  case RI_RESOURCE_MANAGER: return new cCiResourceManager(SessionId, Tc); break;
1727  case RI_APPLICATION_INFORMATION: return new cCiApplicationInformation(SessionId, Tc); break;
1728  case RI_CONDITIONAL_ACCESS_SUPPORT: return new cCiConditionalAccessSupport(SessionId, Tc); break;
1729  case RI_HOST_CONTROL: return new cCiHostControl(SessionId, Tc); break;
1730  case RI_DATE_TIME: return new cCiDateTime(SessionId, Tc); break;
1731  case RI_MMI: return new cCiMMI(SessionId, Tc); break;
1732  default: return NULL;
1733  }
1734 }
1735 
1736 // --- cCiResourceHandlers ---------------------------------------------------
1737 
1739 
1741 {
1743 }
1744 
1746 {
1747  if (ResourceHandler) {
1748  Add(ResourceHandler);
1749  if (const uint32_t *r = ResourceHandler->ResourceIds()) {
1750  while (*r) {
1751  resourceIds.Append(htonl(*r));
1752  r++;
1753  }
1754  }
1755  }
1756 }
1757 
1758 cCiSession *cCiResourceHandlers::GetNewCiSession(uint32_t ResourceId, uint16_t SessionId, cCiTransportConnection *Tc)
1759 {
1760  for (cCiResourceHandler *r = Last(); r; r = Prev(r)) {
1761  if (cCiSession *CiSession = r->GetNewCiSession(ResourceId, SessionId, Tc))
1762  return CiSession;
1763  }
1764  return NULL;
1765 }
1766 
1767 // --- cCiTransportConnection (cont'd) ---------------------------------------
1768 
1769 #define TC_POLL_TIMEOUT 300 // ms WORKAROUND: TC_POLL_TIMEOUT < 300ms doesn't work with DragonCAM
1770 #define TC_ALIVE_TIMEOUT 2000 // ms after which a transport connection is assumed dead
1771 
1773 {
1774  dbgprotocol("Slot %d: creating connection %d/%d\n", CamSlot->SlotNumber(), CamSlot->SlotIndex(), Tcid);
1775  camSlot = CamSlot;
1776  tcid = Tcid;
1777  state = stIDLE;
1778  createConnectionRequested = false;
1779  deleteConnectionRequested = false;
1780  hasUserIO = false;
1782  for (int i = 0; i <= MAX_SESSIONS_PER_TC; i++) // sessions[0] is not used, but initialized anyway
1783  sessions[i] = NULL;
1784  tsPostProcessor = NULL;
1785 }
1786 
1788 {
1789  for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++)
1790  delete sessions[i];
1791 }
1792 
1794 {
1795  tsPostProcessor = CiSession;
1796 }
1797 
1799 {
1800  if (tsPostProcessor)
1801  return tsPostProcessor->TsPostProcess(TsPacket);
1802  return false;
1803 }
1804 
1806 {
1808  return cas && cas->Ready();
1809 }
1810 
1812 {
1814  return ai ? ai->GetMenuString() : NULL;
1815 }
1816 
1817 void cCiTransportConnection::SendTPDU(uint8_t Tag, int Length, const uint8_t *Data)
1818 {
1819  cTPDU TPDU(camSlot->SlotIndex(), tcid, Tag, Length, Data);
1820  camSlot->Write(&TPDU);
1822 }
1823 
1824 void cCiTransportConnection::SendData(int Length, const uint8_t *Data)
1825 {
1826  // if Length ever exceeds MAX_TPDU_DATA this needs to be handled differently
1827  if (state == stACTIVE && Length > 0)
1828  SendTPDU(T_DATA_LAST, Length, Data);
1829 }
1830 
1831 void cCiTransportConnection::SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId, int Status)
1832 {
1833  uint8_t buffer[16];
1834  uint8_t *p = buffer;
1835  *p++ = Tag;
1836  *p++ = 0x00; // will contain length
1837  if (Status >= 0)
1838  *p++ = Status;
1839  if (ResourceId) {
1840  put_unaligned(htonl(ResourceId), (uint32_t *)p);
1841  p += 4;
1842  }
1843  put_unaligned(htons(SessionId), (uint16_t *)p);
1844  p += 2;
1845  buffer[1] = p - buffer - 2; // length
1846  SendData(p - buffer, buffer);
1847 }
1848 
1850 {
1851  bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer;
1853  if (DumpPolls)
1854  dbgprotocol("Slot %d: ==> Poll\n", camSlot->SlotNumber());
1856  DumpTPDUDataTransfer = OldDumpTPDUDataTransfer;
1857 }
1858 
1859 uint32_t cCiTransportConnection::ResourceIdToInt(const uint8_t *Data)
1860 {
1861  return (ntohl(get_unaligned((uint32_t *)Data)));
1862 }
1863 
1865 {
1866  return (SessionId <= MAX_SESSIONS_PER_TC) ? sessions[SessionId] : NULL;
1867 }
1868 
1870 {
1871  cCiSession *CiSession = NULL;
1872  for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
1873  if (cCiSession *s = sessions[i]) {
1874  if (s->ResourceId() == ResourceId)
1875  return s; // prefer exact match
1876  if ((s->ResourceId() & RESOURCE_CLASS_MASK) == (ResourceId & RESOURCE_CLASS_MASK))
1877  CiSession = s;
1878  }
1879  }
1880  return CiSession;
1881 }
1882 
1883 void cCiTransportConnection::OpenSession(int Length, const uint8_t *Data)
1884 {
1885  if (Length == 6 && *(Data + 1) == 0x04) {
1886  uint32_t ResourceId = ResourceIdToInt(Data + 2);
1887  dbgprotocol("Slot %d: open session %08X\n", camSlot->SlotNumber(), ResourceId);
1888  if (!GetSessionByResourceId(ResourceId)) {
1889  for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
1890  if (!sessions[i]) {
1891  sessions[i] = CiResourceHandlers.GetNewCiSession(ResourceId, i, this);
1892  if (sessions[i])
1893  SendTag(ST_OPEN_SESSION_RESPONSE, sessions[i]->SessionId(), sessions[i]->ResourceId(), SS_OK);
1894  else
1895  esyslog("ERROR: CAM %d: unknown resource identifier: %08X (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
1896  return;
1897  }
1898  }
1899  esyslog("ERROR: CAM %d: no free session slot for resource identifier %08X (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
1900  }
1901  else
1902  esyslog("ERROR: CAM %d: session for resource identifier %08X already exists (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
1903  }
1904 }
1905 
1907 {
1908  dbgprotocol("Slot %d: close session %d\n", camSlot->SlotNumber(), SessionId);
1909  cCiSession *Session = GetSessionBySessionId(SessionId);
1910  if (Session && sessions[SessionId] == Session) {
1911  delete Session;
1912  sessions[SessionId] = NULL;
1913  SendTag(ST_CLOSE_SESSION_RESPONSE, SessionId, 0, SS_OK);
1914  }
1915  else {
1916  esyslog("ERROR: CAM %d: unknown session id: %d (%d/%d)", camSlot->SlotNumber(), SessionId, camSlot->SlotIndex(), tcid);
1918  }
1919 }
1920 
1922 {
1923  int Length;
1924  const uint8_t *Data = TPDU->Data(Length);
1925  if (Data && Length > 1) {
1926  switch (*Data) {
1927  case ST_SESSION_NUMBER: if (Length > 4) {
1928  uint16_t SessionId = ntohs(get_unaligned((uint16_t *)&Data[2]));
1929  cCiSession *Session = GetSessionBySessionId(SessionId);
1930  if (Session)
1931  Session->Process(Length - 4, Data + 4);
1932  else
1933  esyslog("ERROR: CAM %d: unknown session id: %d (%d/%d)", camSlot->SlotNumber(), SessionId, camSlot->SlotIndex(), tcid);
1934  }
1935  break;
1936  case ST_OPEN_SESSION_REQUEST: OpenSession(Length, Data);
1937  break;
1938  case ST_CLOSE_SESSION_REQUEST: if (Length == 4)
1939  CloseSession(ntohs(get_unaligned((uint16_t *)&Data[2])));
1940  break;
1941  case ST_CREATE_SESSION_RESPONSE: // not implemented
1942  case ST_CLOSE_SESSION_RESPONSE: // not implemented
1943  default: esyslog("ERROR: CAM %d: unknown session tag: %02X (%d/%d)", camSlot->SlotNumber(), *Data, camSlot->SlotIndex(), tcid);
1944  }
1945  }
1946 }
1947 
1949 {
1950  if (TPDU)
1952  else if (alive.TimedOut())
1953  return false;
1954  switch (state) {
1955  case stIDLE:
1957  dbgprotocol("Slot %d: create connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1958  createConnectionRequested = false;
1960  state = stCREATION;
1961  }
1962  return true;
1963  case stCREATION:
1964  if (TPDU && TPDU->Tag() == T_CTC_REPLY) {
1965  dbgprotocol("Slot %d: connection created %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1966  Poll();
1967  state = stACTIVE;
1968  }
1969  else if (timer.TimedOut()) {
1970  dbgprotocol("Slot %d: timeout while creating connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1971  state = stIDLE;
1972  }
1973  return true;
1974  case stACTIVE:
1976  dbgprotocol("Slot %d: delete connection requested %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1977  deleteConnectionRequested = false;
1979  state = stDELETION;
1980  return true;
1981  }
1982  if (TPDU) {
1983  switch (TPDU->Tag()) {
1984  case T_REQUEST_TC:
1985  esyslog("ERROR: CAM %d: T_REQUEST_TC not implemented (%d/%d)", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1986  break;
1987  case T_DATA_MORE:
1988  case T_DATA_LAST:
1989  HandleSessions(TPDU);
1990  // continue with T_SB
1991  case T_SB:
1992  if ((TPDU->Status() & DATA_INDICATOR) != 0) {
1993  dbgprotocol("Slot %d: receive data %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1994  SendTPDU(T_RCV);
1995  }
1996  break;
1997  case T_DELETE_TC:
1998  dbgprotocol("Slot %d: delete connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
2000  state = stIDLE;
2001  return true;
2002  case T_RCV:
2003  case T_CREATE_TC:
2004  case T_CTC_REPLY:
2005  case T_DTC_REPLY:
2006  case T_NEW_TC:
2007  case T_TC_ERROR:
2008  break;
2009  default:
2010  esyslog("ERROR: unknown TPDU tag: 0x%02X (%s)", TPDU->Tag(), __FUNCTION__);
2011  }
2012  }
2013  else if (timer.TimedOut())
2014  Poll();
2015  hasUserIO = false;
2016  for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
2017  if (sessions[i]) {
2018  sessions[i]->Process();
2019  if (sessions[i]->HasUserIO())
2020  hasUserIO = true;
2021  }
2022  }
2023  break;
2024  case stDELETION:
2025  if (TPDU && TPDU->Tag() == T_DTC_REPLY || timer.TimedOut()) {
2026  dbgprotocol("Slot %d: connection deleted %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
2027  state = stIDLE;
2028  }
2029  return true;
2030  default:
2031  esyslog("ERROR: unknown state: %d (%s)", state, __FUNCTION__);
2032  }
2033  return true;
2034 }
2035 
2036 // --- cCiCaPidData ----------------------------------------------------------
2037 
2038 class cCiCaPidData : public cListObject {
2039 public:
2040  bool active;
2041  int pid;
2043  cCiCaPidData(int Pid, int StreamType)
2044  {
2045  active = false;
2046  pid = Pid;
2047  streamType = StreamType;
2048  }
2049  };
2050 
2051 // --- cCiCaProgramData ------------------------------------------------------
2052 
2054 public:
2056  bool modified;
2058  cCiCaProgramData(int ProgramNumber)
2059  {
2060  programNumber = ProgramNumber;
2061  modified = false;
2062  }
2063  bool Active(void)
2064  {
2065  for (cCiCaPidData *p = pidList.First(); p; p = pidList.Next(p)) {
2066  if (p->active)
2067  return true;
2068  }
2069  return false;
2070  }
2071  };
2072 
2073 // --- cCiAdapter ------------------------------------------------------------
2074 
2076 :cThread("CI adapter")
2077 {
2078  for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++)
2079  camSlots[i] = NULL;
2080 }
2081 
2083 {
2084  Cancel(3);
2085  for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++)
2086  delete camSlots[i];
2087 }
2088 
2090 {
2091  if (CamSlot) {
2092  for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) {
2093  if (!camSlots[i]) {
2094  CamSlot->slotIndex = i;
2095  camSlots[i] = CamSlot;
2096  return;
2097  }
2098  }
2099  esyslog("ERROR: no free CAM slot in CI adapter");
2100  }
2101 }
2102 
2104 {
2105  if (Iter >= 0) {
2106  for (; Iter < MAX_CAM_SLOTS_PER_ADAPTER; ) {
2107  if (cCamSlot *Found = camSlots[Iter++])
2108  return Found;
2109  }
2110  }
2111  return NULL;
2112 }
2113 
2115 {
2116  cTPDU TPDU;
2117  while (Running()) {
2118  int n = Read(TPDU.Buffer(), TPDU.MaxSize());
2119  if (n > 0 && TPDU.Slot() < MAX_CAM_SLOTS_PER_ADAPTER) {
2120  TPDU.SetSize(n);
2121  cCamSlot *cs = camSlots[TPDU.Slot()];
2122  TPDU.Dump(cs ? cs->SlotNumber() : 0, false);
2123  if (cs)
2124  cs->Process(&TPDU);
2125  }
2126  for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) {
2127  if (camSlots[i])
2128  camSlots[i]->Process();
2129  }
2130  }
2131 }
2132 
2133 // --- cCamSlot --------------------------------------------------------------
2134 
2135 #define MODULE_CHECK_INTERVAL 500 // ms
2136 #define MODULE_RESET_TIMEOUT 2 // s
2137 
2138 cCamSlot::cCamSlot(cCiAdapter *CiAdapter, bool WantsTsData, cCamSlot *MasterSlot)
2139 {
2140  ciAdapter = CiAdapter;
2142  assignedDevice = NULL;
2143  caPidReceiver = WantsTsData ? new cCaPidReceiver : NULL;
2144  caActivationReceiver = NULL;
2145  slotIndex = -1;
2146  mtdAvailable = false;
2147  mtdHandler = NULL;
2148  lastModuleStatus = msReset; // avoids initial reset log message
2149  resetTime = 0;
2150  resendPmt = false;
2151  for (int i = 0; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) // tc[0] is not used, but initialized anyway
2152  tc[i] = NULL;
2153  if (MasterSlot)
2155  if (ciAdapter) {
2156  CamSlots.Add(this);
2157  slotNumber = Index() + 1;
2158  ciAdapter->AddCamSlot(this);
2159  Reset();
2160  }
2161 }
2162 
2164 {
2165  Assign(NULL);
2166  delete caPidReceiver;
2167  delete caActivationReceiver;
2168  CamSlots.Del(this, false);
2170  delete mtdHandler;
2171 }
2172 
2174 {
2175  cMutexLock MutexLock(&mutex);
2176  if (mtdHandler)
2177  return mtdHandler->GetMtdCamSlot(this);
2178  return this;
2179 }
2180 
2181 bool cCamSlot::Assign(cDevice *Device, bool Query)
2182 {
2183  cMutexLock MutexLock(&mutex);
2184  if (Device == assignedDevice)
2185  return true;
2186  if (ciAdapter) {
2187  int OldDeviceNumber = 0;
2188  if (assignedDevice && !Query) {
2189  OldDeviceNumber = assignedDevice->DeviceNumber() + 1;
2190  if (caPidReceiver)
2192  assignedDevice->SetCamSlot(NULL);
2193  assignedDevice = NULL;
2194  }
2195  if (ciAdapter->Assign(Device, true)) {
2196  if (!Query) {
2197  StopDecrypting();
2198  if (ciAdapter->Assign(Device)) {
2199  if (Device) {
2200  Device->SetCamSlot(this);
2202  if (caPidReceiver) {
2203  caPidReceiver->Reset();
2205  }
2206  dsyslog("CAM %d: assigned to device %d", MasterSlotNumber(), Device->DeviceNumber() + 1);
2207  }
2208  else {
2209  CancelActivation();
2210  dsyslog("CAM %d: unassigned from device %d", MasterSlotNumber(), OldDeviceNumber);
2211  }
2212  }
2213  else
2214  return false;
2215  }
2216  return true;
2217  }
2218  }
2219  return false;
2220 }
2221 
2223 {
2224  cMutexLock MutexLock(&mutex);
2225  if (mtdHandler)
2226  return mtdHandler->Devices(CardIndexes);
2227  if (assignedDevice)
2228  CardIndexes.Append(assignedDevice->CardIndex());
2229  return CardIndexes.Size() > 0;
2230 }
2231 
2233 {
2234  cMutexLock MutexLock(&mutex);
2235  for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) {
2236  if (!tc[i]) {
2237  tc[i] = new cCiTransportConnection(this, i);
2238  tc[i]->CreateConnection();
2239  return;
2240  }
2241  }
2242  esyslog("ERROR: CAM %d: can't create new transport connection!", slotNumber);
2243 }
2244 
2246 {
2247  cMutexLock MutexLock(&mutex);
2248  for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) {
2249  delete tc[i];
2250  tc[i] = NULL;
2251  }
2252 }
2253 
2255 {
2256  cMutexLock MutexLock(&mutex);
2257  if (TPDU) {
2258  int n = TPDU->Tcid();
2259  if (1 <= n && n <= MAX_CONNECTIONS_PER_CAM_SLOT) {
2260  if (tc[n])
2261  tc[n]->Process(TPDU);
2262  }
2263  }
2264  for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) {
2265  if (tc[i]) {
2266  if (!tc[i]->Process()) {
2267  Reset();
2268  return;
2269  }
2270  }
2271  }
2272  if (moduleCheckTimer.TimedOut()) {
2273  eModuleStatus ms = ModuleStatus();
2274  if (ms != lastModuleStatus) {
2275  switch (ms) {
2276  case msNone:
2277  dbgprotocol("Slot %d: no module present\n", slotNumber);
2278  isyslog("CAM %d: no module present", slotNumber);
2279  StopDecrypting();
2281  CancelActivation();
2282  if (mtdHandler)
2284  else
2285  Assign(NULL);
2286  break;
2287  case msReset:
2288  dbgprotocol("Slot %d: module reset\n", slotNumber);
2289  isyslog("CAM %d: module reset", slotNumber);
2291  break;
2292  case msPresent:
2293  dbgprotocol("Slot %d: module present\n", slotNumber);
2294  isyslog("CAM %d: module present", slotNumber);
2295  break;
2296  case msReady:
2297  dbgprotocol("Slot %d: module ready\n", slotNumber);
2298  isyslog("CAM %d: module ready", slotNumber);
2299  NewConnection();
2300  resendPmt = true;
2301  break;
2302  default:
2303  esyslog("ERROR: unknown module status %d (%s)", ms, __FUNCTION__);
2304  }
2305  lastModuleStatus = ms;
2306  }
2308  }
2309  if (resendPmt && Ready()) {
2310  if (mtdHandler) {
2312  resendPmt = false;
2313  }
2314  else if (caProgramList.Count())
2315  StartDecrypting();
2316  }
2317  processed.Broadcast();
2318 }
2319 
2321 {
2322  cMutexLock MutexLock(&mutex);
2323  return tc[1] ? tc[1]->GetSessionByResourceId(ResourceId) : NULL;
2324 }
2325 
2327 {
2328  cMutexLock MutexLock(&mutex);
2329  if (ciAdapter && TPDU->Size()) {
2330  TPDU->Dump(SlotNumber(), true);
2331  ciAdapter->Write(TPDU->Buffer(), TPDU->Size());
2332  }
2333 }
2334 
2336 {
2337  cMutexLock MutexLock(&mutex);
2340  if (ciAdapter) {
2341  dbgprotocol("Slot %d: reset...", slotNumber);
2342  if (ciAdapter->Reset(slotIndex)) {
2343  resetTime = time(NULL);
2344  dbgprotocol("ok.\n");
2346  return true;
2347  }
2348  dbgprotocol("failed!\n");
2349  }
2350  return false;
2351 }
2352 
2354 {
2355  return ModuleStatus() == msReady;
2356 }
2357 
2359 {
2360  cMutexLock MutexLock(&mutex);
2361  if (!caActivationReceiver) {
2362  if (cDevice *d = Device()) {
2364  if (const cChannel *Channel = Channels->GetByNumber(cDevice::CurrentChannel())) {
2365  caActivationReceiver = new cCaActivationReceiver(Channel, this);
2366  d->AttachReceiver(caActivationReceiver);
2367  dsyslog("CAM %d: activating on device %d with channel %d (%s)", SlotNumber(), d->DeviceNumber() + 1, Channel->Number(), Channel->Name());
2368  }
2369  }
2370  }
2371 }
2372 
2374 {
2375  cMutexLock MutexLock(&mutex);
2376  if (mtdHandler)
2378  else {
2379  delete caActivationReceiver;
2380  caActivationReceiver = NULL;
2381  }
2382 }
2383 
2385 {
2386  if (mtdHandler)
2387  return mtdHandler->IsActivating();
2388  return caActivationReceiver;
2389 }
2390 
2392 {
2393  cMutexLock MutexLock(&mutex);
2395  if (resetTime) {
2396  if (ms <= msReset) {
2397  if (time(NULL) - resetTime < MODULE_RESET_TIMEOUT)
2398  return msReset;
2399  }
2400  resetTime = 0;
2401  }
2402  return ms;
2403 }
2404 
2405 const char *cCamSlot::GetCamName(void)
2406 {
2407  cMutexLock MutexLock(&mutex);
2408  return tc[1] ? tc[1]->GetCamName() : NULL;
2409 }
2410 
2412 {
2413  cMutexLock MutexLock(&mutex);
2414  return ModuleStatus() == msNone || tc[1] && tc[1]->Ready();
2415 }
2416 
2418 {
2420 }
2421 
2423 {
2424  cMutexLock MutexLock(&mutex);
2425  return tc[1] && tc[1]->HasUserIO();
2426 }
2427 
2429 {
2430  cMutexLock MutexLock(&mutex);
2432  return api ? api->EnterMenu() : false;
2433 }
2434 
2436 {
2437  cMutexLock MutexLock(&mutex);
2439  if (mmi) {
2440  cCiMenu *Menu = mmi->Menu();
2441  if (Menu)
2442  Menu->mutex = &mutex;
2443  return Menu;
2444  }
2445  return NULL;
2446 }
2447 
2449 {
2450  cMutexLock MutexLock(&mutex);
2452  if (mmi) {
2453  cCiEnquiry *Enquiry = mmi->Enquiry();
2454  if (Enquiry)
2455  Enquiry->mutex = &mutex;
2456  return Enquiry;
2457  }
2458  return NULL;
2459 }
2460 
2462 {
2463  for (int i = 0; i < caPmts.Size(); i++)
2464  delete caPmts[i];
2465 }
2466 
2467 cCiCaPmt *cCiCaPmtList::Add(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds)
2468 {
2469  cCiCaPmt *p = new cCiCaPmt(CmdId, Source, Transponder, ProgramNumber, CaSystemIds);
2470  caPmts.Append(p);
2471  return p;
2472 }
2473 
2475 {
2476  if (caPmts.RemoveElement(CaPmt))
2477  delete CaPmt;
2478 }
2479 
2481 {
2482  cMutexLock MutexLock(&mutex);
2484  return cas && cas->RepliesToQuery();
2485 }
2486 
2487 void cCamSlot::BuildCaPmts(uint8_t CmdId, cCiCaPmtList &CaPmtList, cMtdMapper *MtdMapper)
2488 {
2489  cMutexLock MutexLock(&mutex);
2490  CaPmtList.caPmts.Clear();
2491  const int *CaSystemIds = GetCaSystemIds();
2492  if (CaSystemIds && *CaSystemIds) {
2493  if (caProgramList.Count()) {
2494  for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
2495  if (p->modified || resendPmt) {
2496  bool Active = p->Active();
2497  cCiCaPmt *CaPmt = CaPmtList.Add(Active ? CmdId : CPCI_NOT_SELECTED, source, transponder, p->programNumber, CaSystemIds);
2498  for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
2499  if (q->active)
2500  CaPmt->AddPid(q->pid, q->streamType);
2501  }
2502  if (caPidReceiver) {
2503  int CaPids[MAXRECEIVEPIDS + 1];
2504  if (GetCaPids(source, transponder, p->programNumber, CaSystemIds, MAXRECEIVEPIDS + 1, CaPids) > 0) {
2505  if (Active)
2506  caPidReceiver->AddPids(CaPids);
2507  else
2508  caPidReceiver->DelPids(CaPids);
2509  }
2510  }
2511  if (RepliesToQuery())
2512  CaPmt->SetListManagement(Active ? CPLM_ADD : CPLM_UPDATE);
2513  if (MtdMapper)
2514  CaPmt->MtdMapPids(MtdMapper);
2515  p->modified = false;
2516  }
2517  }
2518  }
2519  else if (CmdId == CPCI_NOT_SELECTED)
2520  CaPmtList.Add(CmdId, 0, 0, 0, NULL);
2521  }
2522 }
2523 
2525 {
2526  cMutexLock MutexLock(&mutex);
2528  if (cas) {
2529  for (int i = 0; i < CaPmtList.caPmts.Size(); i++)
2530  cas->SendPMT(CaPmtList.caPmts[i]);
2531  }
2532  resendPmt = false;
2533 }
2534 
2535 void cCamSlot::SendCaPmt(uint8_t CmdId)
2536 {
2537  cMutexLock MutexLock(&mutex);
2538  cCiCaPmtList CaPmtList;
2539  BuildCaPmts(CmdId, CaPmtList);
2540  SendCaPmts(CaPmtList);
2541 }
2542 
2544 {
2545  mtdAvailable = true;
2546 }
2547 
2549 {
2550  if (McdAvailable() && MtdAvailable()) {
2551  if (On) {
2552  if (!mtdHandler) {
2553  dsyslog("CAM %d: activating MTD support", SlotNumber());
2554  mtdHandler = new cMtdHandler;
2555  }
2556  }
2557  else if (mtdHandler) {
2558  dsyslog("CAM %d: deactivating MTD support", SlotNumber());
2559  delete mtdHandler;
2560  mtdHandler = NULL;
2561  }
2562  }
2563 }
2564 
2565 int cCamSlot::MtdPutData(uchar *Data, int Count)
2566 {
2567  return mtdHandler->Put(Data, Count);
2568 }
2569 
2571 {
2572  cMutexLock MutexLock(&mutex);
2574  return cas ? cas->GetCaSystemIds() : NULL;
2575 }
2576 
2578 {
2579  if (mtdHandler)
2580  return mtdHandler->Priority();
2581  cDevice *d = Device();
2582  return d ? d->Priority() : IDLEPRIORITY;
2583 }
2584 
2585 bool cCamSlot::ProvidesCa(const int *CaSystemIds)
2586 {
2587  cMutexLock MutexLock(&mutex);
2589  if (cas) {
2590  for (const int *ids = cas->GetCaSystemIds(); ids && *ids; ids++) {
2591  for (const int *id = CaSystemIds; *id; id++) {
2592  if (*id == *ids)
2593  return true;
2594  }
2595  }
2596  }
2597  return false;
2598 }
2599 
2600 void cCamSlot::AddPid(int ProgramNumber, int Pid, int StreamType)
2601 {
2602  cMutexLock MutexLock(&mutex);
2603  cCiCaProgramData *ProgramData = NULL;
2604  for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
2605  if (p->programNumber == ProgramNumber) {
2606  ProgramData = p;
2607  for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
2608  if (q->pid == Pid)
2609  return;
2610  }
2611  }
2612  }
2613  if (!ProgramData)
2614  caProgramList.Add(ProgramData = new cCiCaProgramData(ProgramNumber));
2615  ProgramData->pidList.Add(new cCiCaPidData(Pid, StreamType));
2616 }
2617 
2618 void cCamSlot::SetPid(int Pid, bool Active)
2619 {
2621  return;
2622  cMutexLock MutexLock(&mutex);
2623  for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
2624  for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
2625  if (q->pid == Pid) {
2626  if (q->active != Active) {
2627  q->active = Active;
2628  p->modified = true;
2629  }
2630  return;
2631  }
2632  }
2633  }
2634 }
2635 
2636 // see ISO/IEC 13818-1
2637 #define STREAM_TYPE_VIDEO 0x02
2638 #define STREAM_TYPE_AUDIO 0x04
2639 #define STREAM_TYPE_PRIVATE 0x06
2640 
2641 void cCamSlot::AddChannel(const cChannel *Channel)
2642 {
2643  cMutexLock MutexLock(&mutex);
2644  if (source != Channel->Source() || transponder != Channel->Transponder())
2645  StopDecrypting();
2646  source = Channel->Source();
2647  transponder = Channel->Transponder();
2648  if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
2649  AddPid(Channel->Sid(), Channel->Vpid(), STREAM_TYPE_VIDEO);
2650  for (const int *Apid = Channel->Apids(); *Apid; Apid++)
2651  AddPid(Channel->Sid(), *Apid, STREAM_TYPE_AUDIO);
2652  for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
2653  AddPid(Channel->Sid(), *Dpid, STREAM_TYPE_PRIVATE);
2654  for (const int *Spid = Channel->Spids(); *Spid; Spid++)
2655  AddPid(Channel->Sid(), *Spid, STREAM_TYPE_PRIVATE);
2656  }
2657 }
2658 
2659 #define QUERY_REPLY_WAIT 100 // ms to wait between checks for a reply
2660 
2661 bool cCamSlot::CanDecrypt(const cChannel *Channel, cMtdMapper *MtdMapper)
2662 {
2663  if (Channel->Ca() < CA_ENCRYPTED_MIN)
2664  return true; // channel not encrypted
2665  if (!IsDecrypting())
2666  return true; // any CAM can decrypt at least one channel
2667  cMutexLock MutexLock(&mutex);
2669  if (cas && cas->RepliesToQuery()) {
2670  cCiCaPmt CaPmt(CPCI_QUERY, Channel->Source(), Channel->Transponder(), Channel->Sid(), GetCaSystemIds());
2671  CaPmt.SetListManagement(CPLM_ADD); // WORKAROUND: CPLM_ONLY doesn't work with Alphacrypt 3.09 (deletes existing CA_PMTs)
2672  CaPmt.AddPid(Channel->Vpid(), STREAM_TYPE_VIDEO);
2673  for (const int *Apid = Channel->Apids(); *Apid; Apid++)
2674  CaPmt.AddPid(*Apid, STREAM_TYPE_AUDIO);
2675  for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
2676  CaPmt.AddPid(*Dpid, STREAM_TYPE_PRIVATE);
2677  for (const int *Spid = Channel->Spids(); *Spid; Spid++)
2678  CaPmt.AddPid(*Spid, STREAM_TYPE_PRIVATE);
2679  if (MtdMapper)
2680  CaPmt.MtdMapPids(MtdMapper);
2681  cas->SendPMT(&CaPmt);
2682  cTimeMs Timeout(QUERY_REPLY_TIMEOUT);
2683  do {
2685  if ((cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT)) != NULL) { // must re-fetch it, there might have been a reset
2686  if (cas->ReceivedReply())
2687  return cas->CanDecrypt();
2688  }
2689  else
2690  return false;
2691  } while (!Timeout.TimedOut());
2692  dsyslog("CAM %d: didn't reply to QUERY", SlotNumber());
2693  }
2694  return false;
2695 }
2696 
2698 {
2700 }
2701 
2703 {
2704  cMutexLock MutexLock(&mutex);
2705  if (caProgramList.Count()) {
2706  caProgramList.Clear();
2707  if (!dynamic_cast<cMtdCamSlot *>(this))
2709  }
2710 }
2711 
2713 {
2714  cMutexLock MutexLock(&mutex);
2715  if (mtdHandler)
2716  return mtdHandler->IsDecrypting();
2717  if (caProgramList.Count()) {
2718  for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
2719  if (p->modified)
2720  return true; // any modifications need to be processed before we can assume it's no longer decrypting
2721  for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
2722  if (q->active)
2723  return true;
2724  }
2725  }
2726  }
2727  return false;
2728 }
2729 
2730 uchar *cCamSlot::Decrypt(uchar *Data, int &Count)
2731 {
2732  if (Data)
2733  Count = TS_SIZE;
2734  return Data;
2735 }
2736 
2738 {
2739  return tc[1] ? tc[1]->TsPostProcess(Data) : false;
2740 }
2741 
2742 bool cCamSlot::Inject(uchar *Data, int Count)
2743 {
2744  return true;
2745 }
2746 
2747 void cCamSlot::InjectEit(int Sid)
2748 {
2749  cEitGenerator Eit(Sid);
2750  Inject(Eit.Data(), Eit.Length());
2751 }
2752 
2753 // --- cCamSlots -------------------------------------------------------------
2754 
2756 
2758 {
2759  int n = 0;
2760  for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
2761  if (CamSlot->IsMasterSlot() && CamSlot->ModuleStatus() == msReady)
2762  n++;
2763  }
2764  return n;
2765 }
2766 
2768 {
2769  bool ready = true;
2770  for (time_t t0 = time(NULL); time(NULL) - t0 < Timeout; ) {
2771  ready = true;
2772  for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
2773  if (!CamSlot->Ready()) {
2774  ready = false;
2775  cCondWait::SleepMs(100);
2776  }
2777  }
2778  if (ready)
2779  break;
2780  }
2781  for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot))
2782  dsyslog("CAM %d: %sready, %s", CamSlot->SlotNumber(), CamSlot->Ready() ? "" : "not ", CamSlot->IsMasterSlot() ? *cString::sprintf("master (%s)", CamSlot->GetCamName() ? CamSlot->GetCamName() : "empty") : *cString::sprintf("slave of CAM %d", CamSlot->MasterSlotNumber()));
2783  return ready;
2784 }
2785 
2786 // --- cChannelCamRelation ---------------------------------------------------
2787 
2788 #define CAM_CHECKED_TIMEOUT 15 // seconds before a CAM that has been checked for a particular channel will be checked again
2789 
2791 private:
2795  time_t lastChecked;
2796 public:
2798  bool TimedOut(void);
2799  tChannelID ChannelID(void) { return channelID; }
2800  bool CamChecked(int CamSlotNumber);
2801  bool CamDecrypt(int CamSlotNumber);
2802  void SetChecked(int CamSlotNumber);
2803  void SetDecrypt(int CamSlotNumber);
2804  void ClrChecked(int CamSlotNumber);
2805  void ClrDecrypt(int CamSlotNumber);
2806  };
2807 
2809 {
2810  channelID = ChannelID;
2811  camSlotsChecked = 0;
2812  camSlotsDecrypt = 0;
2813  lastChecked = 0;
2814 }
2815 
2817 {
2818  return !camSlotsDecrypt && time(NULL) - lastChecked > CAM_CHECKED_TIMEOUT;
2819 }
2820 
2821 bool cChannelCamRelation::CamChecked(int CamSlotNumber)
2822 {
2823  if (lastChecked && time(NULL) - lastChecked > CAM_CHECKED_TIMEOUT) {
2824  lastChecked = 0;
2825  camSlotsChecked = 0;
2826  }
2827  return camSlotsChecked & (1 << (CamSlotNumber - 1));
2828 }
2829 
2830 bool cChannelCamRelation::CamDecrypt(int CamSlotNumber)
2831 {
2832  return camSlotsDecrypt & (1 << (CamSlotNumber - 1));
2833 }
2834 
2835 void cChannelCamRelation::SetChecked(int CamSlotNumber)
2836 {
2837  camSlotsChecked |= (1 << (CamSlotNumber - 1));
2838  lastChecked = time(NULL);
2839  ClrDecrypt(CamSlotNumber);
2840 }
2841 
2842 void cChannelCamRelation::SetDecrypt(int CamSlotNumber)
2843 {
2844  camSlotsDecrypt |= (1 << (CamSlotNumber - 1));
2845  ClrChecked(CamSlotNumber);
2846 }
2847 
2848 void cChannelCamRelation::ClrChecked(int CamSlotNumber)
2849 {
2850  camSlotsChecked &= ~(1 << (CamSlotNumber - 1));
2851  lastChecked = 0;
2852 }
2853 
2854 void cChannelCamRelation::ClrDecrypt(int CamSlotNumber)
2855 {
2856  camSlotsDecrypt &= ~(1 << (CamSlotNumber - 1));
2857 }
2858 
2859 // --- cChannelCamRelations --------------------------------------------------
2860 
2861 #define MAX_CAM_NUMBER 32
2862 #define CHANNEL_CAM_RELATIONS_CLEANUP_INTERVAL 3600 // seconds between cleanups
2863 
2865 
2867 {
2868  lastCleanup = time(NULL);
2869 }
2870 
2872 {
2873  cMutexLock MutexLock(&mutex);
2875  for (cChannelCamRelation *ccr = First(); ccr; ) {
2876  cChannelCamRelation *c = ccr;
2877  ccr = Next(ccr);
2878  if (c->TimedOut())
2879  Del(c);
2880  }
2881  lastCleanup = time(NULL);
2882  }
2883 }
2884 
2886 {
2887  cMutexLock MutexLock(&mutex);
2888  Cleanup();
2889  for (cChannelCamRelation *ccr = First(); ccr; ccr = Next(ccr)) {
2890  if (ccr->ChannelID() == ChannelID)
2891  return ccr;
2892  }
2893  return NULL;
2894 }
2895 
2897 {
2898  cMutexLock MutexLock(&mutex);
2899  cChannelCamRelation *ccr = GetEntry(ChannelID);
2900  if (!ccr)
2901  Add(ccr = new cChannelCamRelation(ChannelID));
2902  return ccr;
2903 }
2904 
2905 void cChannelCamRelations::Reset(int CamSlotNumber)
2906 {
2907  cMutexLock MutexLock(&mutex);
2908  for (cChannelCamRelation *ccr = First(); ccr; ccr = Next(ccr)) {
2909  ccr->ClrChecked(CamSlotNumber);
2910  ccr->ClrDecrypt(CamSlotNumber);
2911  }
2912 }
2913 
2914 bool cChannelCamRelations::CamChecked(tChannelID ChannelID, int CamSlotNumber)
2915 {
2916  cMutexLock MutexLock(&mutex);
2917  cChannelCamRelation *ccr = GetEntry(ChannelID);
2918  return ccr ? ccr->CamChecked(CamSlotNumber) : false;
2919 }
2920 
2921 bool cChannelCamRelations::CamDecrypt(tChannelID ChannelID, int CamSlotNumber)
2922 {
2923  cMutexLock MutexLock(&mutex);
2924  cChannelCamRelation *ccr = GetEntry(ChannelID);
2925  return ccr ? ccr->CamDecrypt(CamSlotNumber) : false;
2926 }
2927 
2928 void cChannelCamRelations::SetChecked(tChannelID ChannelID, int CamSlotNumber)
2929 {
2930  cMutexLock MutexLock(&mutex);
2931  cChannelCamRelation *ccr = AddEntry(ChannelID);
2932  if (ccr)
2933  ccr->SetChecked(CamSlotNumber);
2934 }
2935 
2936 void cChannelCamRelations::SetDecrypt(tChannelID ChannelID, int CamSlotNumber)
2937 {
2938  cMutexLock MutexLock(&mutex);
2939  cChannelCamRelation *ccr = AddEntry(ChannelID);
2940  if (ccr)
2941  ccr->SetDecrypt(CamSlotNumber);
2942 }
2943 
2944 void cChannelCamRelations::ClrChecked(tChannelID ChannelID, int CamSlotNumber)
2945 {
2946  cMutexLock MutexLock(&mutex);
2947  cChannelCamRelation *ccr = GetEntry(ChannelID);
2948  if (ccr)
2949  ccr->ClrChecked(CamSlotNumber);
2950 }
2951 
2952 void cChannelCamRelations::ClrDecrypt(tChannelID ChannelID, int CamSlotNumber)
2953 {
2954  cMutexLock MutexLock(&mutex);
2955  cChannelCamRelation *ccr = GetEntry(ChannelID);
2956  if (ccr)
2957  ccr->ClrDecrypt(CamSlotNumber);
2958 }
2959 
2960 void cChannelCamRelations::Load(const char *FileName)
2961 {
2962  cMutexLock MutexLock(&mutex);
2963  fileName = FileName;
2964  if (access(fileName, R_OK) == 0) {
2965  dsyslog("loading %s", *fileName);
2966  if (FILE *f = fopen(fileName, "r")) {
2967  cReadLine ReadLine;
2968  char *s;
2969  while ((s = ReadLine.Read(f)) != NULL) {
2970  if (char *p = strchr(s, ' ')) {
2971  *p = 0;
2972  if (*++p) {
2973  tChannelID ChannelID = tChannelID::FromString(s);
2974  if (ChannelID.Valid()) {
2975  char *q;
2976  char *strtok_next;
2977  while ((q = strtok_r(p, " ", &strtok_next)) != NULL) {
2978  int CamSlotNumber = atoi(q);
2979  if (CamSlotNumber >= 1 && CamSlotNumber <= MAX_CAM_NUMBER)
2980  SetDecrypt(ChannelID, CamSlotNumber);
2981  p = NULL;
2982  }
2983  }
2984  }
2985  }
2986  }
2987  fclose(f);
2988  }
2989  else
2991  }
2992 }
2993 
2995 {
2996  if (!*fileName)
2997  return;
2998  cMutexLock MutexLock(&mutex);
2999  struct stat st;
3000  if (stat(fileName, &st) == 0) {
3001  if ((st.st_mode & S_IWUSR) == 0) {
3002  dsyslog("not saving %s (file is read-only)", *fileName);
3003  return;
3004  }
3005  }
3006  dsyslog("saving %s", *fileName);
3007  cSafeFile f(fileName);
3008  if (f.Open()) {
3009  for (cChannelCamRelation *ccr = First(); ccr; ccr = Next(ccr)) {
3010  if (ccr->ChannelID().Valid()) {
3011  cString s;
3012  for (int i = 1; i <= MAX_CAM_NUMBER; i++) {
3013  if (ccr->CamDecrypt(i))
3014  s = cString::sprintf("%s%s%d", *s ? *s : "", *s ? " " : "", i);
3015  }
3016  if (*s)
3017  fprintf(f, "%s %s\n", *ccr->ChannelID().ToString(), *s);
3018  }
3019  }
3020  f.Close();
3021  }
3022  else
3024 }
cCiHostControl(uint16_t SessionId, cCiTransportConnection *Tc)
Definition: ci.c:1238
uchar * Data(void)
Definition: remux.h:448
uint16_t applicationManufacturer
Definition: ci.h:72
Definition: ci.h:170
virtual void Process(int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:1308
cTimeMs alive
Definition: ci.c:586
cCiCaProgramData(int ProgramNumber)
Definition: ci.c:2058
#define RI_HOST_CONTROL
Definition: ci.c:637
#define AOT_DISPLAY_CONTROL
Definition: ci.c:661
#define MAXRECEIVEPIDS
Definition: receiver.h:15
int programNumber
Definition: ci.c:2055
unsigned char uchar
Definition: tools.h:31
#define EF_BLIND
Definition: ci.c:1362
static int MtdMapStream(uchar *p, cMtdMapper *MtdMapper)
Definition: ci.c:1013
cMutex mutex
Definition: ci.h:238
bool CanDecrypt(void)
Definition: ci.c:1091
int action
Definition: ci.c:334
virtual void Action(void)
Handles the attached CAM slots in a separate thread.
Definition: ci.c:2114
#define T_SB
Definition: ci.c:459
cCamSlot * MtdSpawn(void)
If this CAM slot can do MTD ("Multi Transponder Decryption"), a call to this function returns a cMtdC...
Definition: ci.c:2173
bool Devices(cVector< int > &CardIndexes)
Adds the card indexes of any devices that currently use this CAM to the given CardIndexes.
Definition: ci.c:2222
bool Process(cTPDU *TPDU=NULL)
Definition: ci.c:1948
#define CRA_SELECT
Definition: ci.c:328
#define AOT_PROFILE_ENQ
Definition: ci.c:644
static tChannelID FromString(const char *s)
Definition: channels.c:24
#define dsyslog(a...)
Definition: tools.h:37
int pid
Definition: ci.c:2041
cCamSlot * CamSlot(void) const
Returns the CAM slot that is currently used with this device, or NULL if no CAM slot is in use...
Definition: device.h:469
Definition: ci.h:492
uint16_t sessionId
Definition: ci.h:34
#define CA_ENCRYPTED_MIN
Definition: channels.h:44
cCiCaPidData(int Pid, int StreamType)
Definition: ci.c:2043
bool isnumber(const char *s)
Definition: tools.c:346
cVector< cCiCaPmt * > caPmts
Definition: ci.h:226
int MasterSlotNumber(void)
Returns the number of this CAM&#39;s master slot within the whole system.
Definition: ci.h:346
virtual bool ProvidesCa(const int *CaSystemIds)
Returns true if the CAM in this slot provides one of the given CaSystemIds.
Definition: ci.c:2585
cCondVar processed
Definition: ci.h:239
static char * CopyString(int Length, const uint8_t *Data)
Definition: ci.c:77
void Set(int Ms=0)
Definition: tools.c:774
bool Devices(cVector< int > &CardIndexes)
Adds the card indexes of the devices of any active MTD CAM slots to the given CardIndexes.
Definition: mtd.c:130
bool Close(void)
Definition: tools.c:1750
#define MAXCASYSTEMIDS
Definition: ci.c:882
virtual ~cCiAdapter()
The derived class must call Cancel(3) in its destructor.
Definition: ci.c:2082
uint8_t Tcid(void) const
Definition: ci.c:604
virtual bool HasUserIO(void)
Definition: ci.c:1378
const uint32_t * Ids(void)
Definition: ci.h:110
virtual void StartActivation(void)
Puts the CAM in this slot into a mode where an inserted smart card can be activated.
Definition: ci.c:2358
cCamSlot * ItCamSlot(int &Iter)
Iterates over all added CAM slots of this adapter.
Definition: ci.c:2103
#define MAX_TPDU_SIZE
Definition: ci.c:454
#define T_CREATE_TC
Definition: ci.c:461
bool RemoveElement(const T &Data)
Definition: tools.h:759
cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc)
Definition: ci.c:694
cList< cCiCaPidData > pidList
Definition: ci.c:2057
virtual void AddPid(int ProgramNumber, int Pid, int StreamType)
Adds the given PID information to the list of PIDs.
Definition: ci.c:2600
bool HasUpdate(void)
Definition: ci.c:1628
virtual void Write(const uint8_t *Buffer, int Length)
Writes Length bytes of the given Buffer.
Definition: ci.h:192
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:2152
uint16_t Peek13(const uchar *p)
Definition: tools.h:258
int source
Definition: ci.h:252
#define CRA_CONFIRM
Definition: ci.c:327
int esInfoLengthPos
Definition: ci.c:904
#define AOT_PROFILE_CHANGE
Definition: ci.c:646
bool CamChecked(int CamSlotNumber)
Definition: ci.c:2821
cCiMenu * menu
Definition: ci.c:1372
#define T_REQUEST_TC
Definition: ci.c:465
cTPDU(void)
Definition: ci.c:477
#define AOT_NONE
Definition: ci.c:643
#define CATPID
Definition: remux.h:53
cCiApplicationInformation(uint16_t SessionId, cCiTransportConnection *Tc)
Definition: ci.c:824
virtual void InjectEit(int Sid)
Injects a generated EIT with a "present event" for the given Sid into the TS data stream sent to the ...
Definition: ci.c:2747
bool Open(void)
Definition: tools.c:1740
const char * GetMenuString(void)
Definition: ci.h:80
virtual void StartDecrypting(void)
Sends all CA_PMT entries to the CAM that have been modified since the last call to this function...
Definition: ci.c:2697
int caSystemIds[MAXCASYSTEMIDS+1]
Definition: ci.c:1079
bool TsPayloadStart(const uchar *p)
Definition: remux.h:77
#define AOT_ENTER_MENU
Definition: ci.c:649
#define MAX_CONNECTIONS_PER_CAM_SLOT
Definition: ci.h:21
static char * GetString(int &Length, const uint8_t **Data)
Definition: ci.c:96
static int MtdMapCaDescriptors(uchar *p, cMtdMapper *MtdMapper)
Definition: ci.c:996
void MtdActivate(bool On)
Activates (On == true) or deactivates (On == false) MTD.
Definition: ci.c:2548
#define MAX_CAM_SLOTS_PER_ADAPTER
Definition: ci.h:20
#define AOT_APPLICATION_INFO_ENQ
Definition: ci.c:647
cCiResourceHandlers CiResourceHandlers
Definition: ci.c:1738
virtual bool TsPostProcess(uchar *Data)
If there is a cCiSession that needs to do additional processing on TS packets (after the CAM has done...
Definition: ci.c:2737
virtual void Receive(const uchar *Data, int Length)
This function is called from the cDevice we are attached to, and delivers one TS packet from the set ...
Definition: ci.c:302
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1127
bool MtdAvailable(void)
Returns true if this CAM supports MTD ("Multi Transponder Decryption").
Definition: ci.h:285
cCamResponses CamResponses
Definition: ci.c:445
#define CHANNEL_CAM_RELATIONS_CLEANUP_INTERVAL
Definition: ci.c:2862
virtual void Append(T Data)
Definition: tools.h:737
Definition: ci.h:148
int QueueMessage(eMessageType Type, const char *s, int Seconds=0, int Timeout=0)
Like Message(), but this function may be called from a background thread.
Definition: skins.c:293
int slotNumber
Definition: ci.h:246
int Source(void) const
Definition: channels.h:152
virtual void Process(int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:1104
int NumReadyMasterSlots(void)
Returns the number of master CAM slots in the system that are ready to decrypt.
Definition: ci.c:2757
void SetCamSlot(cCamSlot *CamSlot)
Sets the given CamSlot to be used with this device.
Definition: device.c:440
#define T_CTC_REPLY
Definition: ci.c:462
char * bottomText
Definition: ci.h:129
#define SIZE_INDICATOR
Definition: ci.c:40
#define RESOURCE_CLASS_MASK
Definition: ci.c:692
static bool DumpDateTime
Definition: ci.c:34
bool SendCloseMMI(void)
Definition: ci.c:1589
#define esyslog(a...)
Definition: tools.h:35
#define QUERY_WAIT_TIME
Definition: ci.c:1071
#define dbgprotocol(a...)
Definition: ci.c:36
void Select(int Index)
Definition: ci.c:1634
#define AOT_ANSW
Definition: ci.c:668
#define ST_CLOSE_SESSION_RESPONSE
Definition: ci.c:625
virtual void Process(int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:784
void Detach(cFilter *Filter)
Detaches the given filter from this device.
Definition: device.c:699
cCamSlot * CamSlot(void)
Definition: ci.c:758
#define CRA_DISCARD
Definition: ci.c:326
int camNumber
Definition: ci.c:332
int Index(void) const
Definition: tools.c:2072
Definition: ci.h:170
bool AttachReceiver(cReceiver *Receiver)
Attaches the given receiver to this device.
Definition: device.c:1748
void MtdEnable(void)
Enables MTD support for this CAM.
Definition: ci.c:2543
virtual bool CanActivate(void)
Returns true if there is a CAM in this slot that can be put into activation mode. ...
Definition: ci.c:2353
static bool DumpTPDUDataTransfer
Definition: ci.c:31
#define DCC_SET_MMI_MODE
Definition: ci.c:1337
#define LOG_ERROR_STR(s)
Definition: tools.h:40
cMutex mutex
Definition: ci.c:125
bool AddPids(const int *Pids)
Adds the given zero terminated list of Pids to the list of PIDs of this receiver. ...
Definition: receiver.c:60
uint8_t ListManagement(void)
Definition: ci.c:916
bool Load(const char *FileName=NULL, bool AllowComments=false, bool MustExist=false)
Definition: config.h:120
#define AOT_CLOSE_MMI
Definition: ci.c:660
virtual bool Inject(uchar *Data, int Count)
Sends all Count bytes of the given Data to the CAM, and returns true if this was possible.
Definition: ci.c:2742
int source
Definition: ci.c:907
cTimeMs timer
Definition: ci.c:587
bool Parse(const char *s)
Definition: ci.c:354
int GetTag(int &Length, const uint8_t **Data)
Definition: ci.c:715
#define CPCI_NOT_SELECTED
Definition: ci.c:898
virtual void Process(int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:1424
bool resendPmt
Definition: ci.h:251
uint8_t tcid
Definition: ci.c:581
#define CPLM_ADD
Definition: ci.c:890
#define AOT_APPLICATION_INFO
Definition: ci.c:648
cCiMMI * mmi
Definition: ci.h:152
const int * GetCaSystemIds(void)
Definition: ci.c:1086
cDevice * Device(void)
Definition: receiver.h:32
const int * Spids(void) const
Definition: channels.h:159
#define STREAM_TYPE_VIDEO
Definition: ci.c:2637
friend class cCiTransportConnection
Definition: ci.h:234
virtual ~cCaPidReceiver()
Definition: ci.c:131
uint32_t resourceId
Definition: ci.h:35
#define MINPRIORITY
Definition: config.h:40
time_t resetTime
Definition: ci.h:249
void Set(int Index, uchar Data)
Definition: tools.h:839
bool Selectable(void)
Definition: ci.h:141
#define AI_CANCEL
Definition: ci.c:1366
cCiSession * GetSessionByResourceId(uint32_t ResourceId)
Definition: ci.c:1869
char * GetText(int &Length, const uint8_t **Data)
Definition: ci.c:1408
cCiAdapter(void)
Definition: ci.c:2075
Definition: ci.c:1369
int SlotIndex(void)
Returns the index of this CAM slot within its CI adapter.
Definition: ci.h:340
cCiSession * GetNewCiSession(uint32_t ResourceId, uint16_t SessionId, cCiTransportConnection *Tc)
Definition: ci.c:1758
cCamSlot * camSlot
Definition: ci.c:580
T min(T a, T b)
Definition: tools.h:59
int Ca(int Index=0) const
Definition: channels.h:173
int slotIndex
Definition: ci.h:245
cCaPidReceiver(void)
Definition: ci.c:146
#define ST_SESSION_NUMBER
Definition: ci.c:619
const uint8_t * GetData(const uint8_t *Data, int &Length)
Definition: ci.c:554
void SetTsPostProcessor(void)
If this cCiSession implements the TsPostProcess() function, it shall call SetTsPostProcessor() to reg...
Definition: ci.c:710
bool CamDecrypt(int CamSlotNumber)
Definition: ci.c:2830
void NewConnection(void)
Definition: ci.c:2232
virtual eModuleStatus ModuleStatus(void)
Returns the status of the CAM in this slot.
Definition: ci.c:2391
int numEntries
Definition: ci.h:131
void AddPid(int Pid, uint8_t StreamType)
Definition: ci.c:949
cCiSession * sessions[MAX_SESSIONS_PER_TC+1]
Definition: ci.c:588
virtual void AddChannel(const cChannel *Channel)
Adds all PIDs of the given Channel to the current list of PIDs.
Definition: ci.c:2641
uint32_t ResourceId(void)
Definition: ci.h:53
const int * Apids(void) const
Definition: channels.h:157
void Write(cTPDU *TPDU)
Definition: ci.c:2326
#define AOT_TUNE
Definition: ci.c:654
char * Read(FILE *f)
Definition: tools.c:1459
void SetListManagement(uint8_t ListManagement)
Definition: ci.c:944
#define MALLOC(type, size)
Definition: tools.h:47
#define RI_CONDITIONAL_ACCESS_SUPPORT
Definition: ci.c:636
void Cleanup(void)
Definition: ci.c:2871
virtual void Clear(void)
Definition: tools.c:2229
int TsPid(const uchar *p)
Definition: remux.h:87
virtual void SendCaPmt(uint8_t CmdId)
Definition: ci.c:2535
bool McdAvailable(void)
Returns true if this CAM supports MCD ("Multi Channel Decyption").
Definition: ci.h:283
#define AOT_CA_INFO_ENQ
Definition: ci.c:650
uint32_t camSlotsChecked
Definition: ci.c:2793
void Reset(void)
Definition: ci.c:134
virtual bool Reset(int Slot)
Resets the CAM in the given Slot.
Definition: ci.h:194
#define AOT_TEXT_LAST
Definition: ci.c:663
cCiTransportConnection * tc[MAX_CONNECTIONS_PER_CAM_SLOT+1]
Definition: ci.h:247
#define MAX_CAM_NUMBER
Definition: ci.c:2861
#define CRA_NONE
Definition: ci.c:325
uint8_t applicationType
Definition: ci.h:71
void CreateConnection(void)
Definition: ci.c:605
int GetMatch(int CamNumber, const char *Text) const
Definition: ci.c:433
cCaActivationReceiver * caActivationReceiver
Definition: ci.h:244
void SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId=0, int Status=-1)
Definition: ci.c:1831
static int MtdMapCaDescriptor(uchar *p, cMtdMapper *MtdMapper)
Definition: ci.c:979
T get_unaligned(T *p)
Definition: tools.h:80
#define MM_HIGH_LEVEL
Definition: ci.c:1345
void CancelActivation(void)
Tells all active MTD CAM slots to cancel activation.
Definition: mtd.c:115
bool blind
Definition: ci.h:155
#define AOT_MENU_LAST
Definition: ci.c:669
void MtdMapPid(uchar *p, cMtdMapper *MtdMapper)
Definition: mtd.c:230
void AddEmmPid(int Pid)
Definition: ci.c:158
int Transponder(void) const
Returns the transponder frequency in MHz, plus the polarization in case of sat.
Definition: channels.c:147
char * titleText
Definition: ci.h:127
#define MODULE_RESET_TIMEOUT
Definition: ci.c:2136
#define MAX_DUMP
#define IDLEPRIORITY
Definition: config.h:43
bool HasUserIO(void)
Definition: ci.c:609
static int CurrentChannel(void)
Returns the number of the current channel on the primary device.
Definition: device.h:350
void DelPids(const int *Pids)
Deletes the given zero terminated list of Pids from the list of PIDs of this receiver.
Definition: receiver.c:106
cCamSlot * masterSlot
Definition: ci.h:241
#define ST_CLOSE_SESSION_REQUEST
Definition: ci.c:624
cCiTransportConnection * tc
Definition: ci.h:36
int streamType
Definition: ci.c:2042
cCiSession * GetSessionByResourceId(uint32_t ResourceId)
Definition: ci.c:2320
int caSystemIds[MAXCASYSTEMIDS+1]
Definition: ci.c:910
virtual void Receive(const uchar *Data, int Length)
This function is called from the cDevice we are attached to, and delivers one TS packet from the set ...
Definition: ci.c:181
virtual void Process(int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:1244
const int * Dpids(void) const
Definition: channels.h:158
~cCiMenu()
Definition: ci.c:1607
virtual ~cCamSlot()
Definition: ci.c:2163
cChannelCamRelation(tChannelID ChannelID)
Definition: ci.c:2808
uint8_t Tag(void)
Definition: ci.c:481
int length
Definition: ci.c:124
char * subTitleText
Definition: ci.h:128
time_t lastScrambledTime
Definition: ci.c:280
virtual cCiSession * GetNewCiSession(uint32_t ResourceId, uint16_t SessionId, cCiTransportConnection *Tc)
Returns a new cCiSession, according to the given ResourceId.
Definition: ci.c:1723
uint32_t ResourceIdToInt(const uint8_t *Data)
Definition: ci.c:1859
bool Ready(void)
Definition: ci.c:1805
int programNumber
Definition: ci.c:909
#define CPLM_UPDATE
Definition: ci.c:891
#define QUERY_REPLY_WAIT
Definition: ci.c:2659
void HandleSessions(cTPDU *TPDU)
Definition: ci.c:1921
cCiEnquiry(cCiMMI *MMI)
Definition: ci.c:1655
void Process(cTPDU *TPDU=NULL)
Definition: ci.c:2254
#define MAX_TPDU_DATA
Definition: ci.c:455
int MtdPutData(uchar *Data, int Count)
Sends at most Count bytes of the given Data to the individual MTD CAM slots that are using this CAM...
Definition: ci.c:2565
virtual ~cCiResourceHandler()
Definition: ci.c:1697
void GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, cDynamicBuffer &Buffer, int EsPid)
Gets all CA descriptors for a given channel.
Definition: pat.c:268
virtual bool HasUserIO(void)
Returns true if there is a pending user interaction, which shall be retrieved via GetMenu() or GetEnq...
Definition: ci.c:2422
void DelEmmPids(void)
Definition: ci.c:171
uint16_t SessionId(void)
Definition: ci.h:52
void SetDecrypt(tChannelID ChannelID, int CamSlotNumber)
Definition: ci.c:2936
void SendData(int Tag, int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:736
cCiEnquiry * enquiry
Definition: ci.c:1373
void Broadcast(void)
Definition: thread.c:150
cCiSession * GetSessionBySessionId(uint16_t SessionId)
Definition: ci.c:1864
cDynamicBuffer capmt
Definition: ci.c:906
void SendDateTime(void)
Definition: ci.c:1283
int Sid(void) const
Definition: channels.h:176
virtual cCiMenu * GetMenu(void)
Gets a pending menu, or NULL if there is no menu.
Definition: ci.c:2435
int Size(void) const
Definition: tools.h:717
bool handlingPid
Definition: ci.c:126
bool SendAnswer(const char *Text)
Definition: ci.c:1577
void StartDecrypting(void)
Tells all active MTD CAM slots to start decrypting.
Definition: mtd.c:105
#define LOCK_CHANNELS_READ
Definition: channels.h:267
cCiConditionalAccessSupport(uint16_t SessionId, cCiTransportConnection *Tc)
Definition: ci.c:1094
#define AI_ANSWER
Definition: ci.c:1367
Definition: ci.h:32
int Length(void)
Definition: tools.h:843
int expectedLength
Definition: ci.h:156
#define ST_OPEN_SESSION_RESPONSE
Definition: ci.c:621
uint8_t Tcid(void)
Definition: ci.c:480
#define RI_MMI
Definition: ci.c:639
int size
Definition: ci.c:473
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition: thread.c:72
#define AOT_REPLACE
Definition: ci.c:655
void Cancel(void)
Definition: ci.c:1641
cList< cCiCaProgramData > caProgramList
Definition: ci.h:254
bool EnterMenu(void)
Definition: ci.c:870
#define STREAM_TYPE_AUDIO
Definition: ci.c:2638
cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds)
Definition: ci.c:921
#define AOT_CA_INFO
Definition: ci.c:651
#define AOT_PROFILE
Definition: ci.c:645
Definition: ci.h:172
#define ST_OPEN_SESSION_REQUEST
Definition: ci.c:620
uint8_t Slot(void)
Definition: ci.c:479
cDynamicBuffer caDescriptors
Definition: ci.c:905
void MtdMapPids(cMtdMapper *MtdMapper)
Definition: ci.c:1038
virtual ~cCiTransportConnection()
Definition: ci.c:1787
#define CA_ENABLE(x)
Definition: ci.c:1069
cCiEnquiry * Enquiry(bool Clear=false)
Definition: ci.c:1560
int Matches(int CamNumber, const char *Text) const
Definition: ci.c:417
#define AOT_LIST_LAST
Definition: ci.c:672
bool WantsTsData(void) const
Returns true if this CAM slot wants to receive the TS data through its Decrypt() function.
Definition: ci.h:337
Definition: skins.h:37
cMtdCamSlot * GetMtdCamSlot(cCamSlot *MasterSlot)
Creates a new MTD CAM slot, or reuses an existing one that is currently unused.
Definition: mtd.c:46
#define AOT_DISPLAY_REPLY
Definition: ci.c:662
void ClrChecked(int CamSlotNumber)
Definition: ci.c:2848
cVector< uint32_t > resourceIds
Definition: ci.h:101
int Size(void)
Definition: ci.c:485
bool CamDecrypt(tChannelID ChannelID, int CamSlotNumber)
Definition: ci.c:2921
virtual int Read(uint8_t *Buffer, int MaxLength)
Reads one chunk of data into the given Buffer, up to MaxLength bytes.
Definition: ci.h:187
bool Active(void)
Definition: ci.c:2063
void Load(const char *FileName)
Definition: ci.c:2960
uint16_t manufacturerCode
Definition: ci.h:73
#define MAX_SESSIONS_PER_TC
Definition: ci.c:575
eModuleStatus lastModuleStatus
Definition: ci.h:248
#define STREAM_TYPE_PRIVATE
Definition: ci.c:2639
Definition: ci.h:232
#define CPLM_ONLY
Definition: ci.c:889
uint32_t camSlotsDecrypt
Definition: ci.c:2794
void AddCamSlot(cCamSlot *CamSlot)
Adds the given CamSlot to this CI adapter.
Definition: ci.c:2089
cVector< int > emmPids
Definition: ci.c:120
void BuildCaPmts(uint8_t CmdId, cCiCaPmtList &CaPmtList, cMtdMapper *MtdMapper=NULL)
Generates all CA_PMTs with the given CmdId and stores them in the given CaPmtList.
Definition: ci.c:2487
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition: thread.h:101
cCiAdapter * ciAdapter
Definition: ci.h:240
const cCiResourceHandler * Last(void) const
Returns the last element in this list, or NULL if the list is empty.
Definition: tools.h:608
cCiTransportConnection * Tc(void)
Definition: ci.h:48
bool active
Definition: ci.c:2040
uint8_t CmdId(void)
Definition: ci.c:914
Definition: thread.h:67
bool HandlingPid(void)
The cCaPidReceiver adds/deletes PIDs to/from the base class cReceiver, which in turn does the same on...
Definition: ci.c:263
#define AOT_ENQ
Definition: ci.c:667
bool TimedWait(cMutex &Mutex, int TimeoutMs)
Definition: thread.c:132
Definition: ci.c:471
cCiSession * tsPostProcessor
Definition: ci.c:589
void OpenSession(int Length, const uint8_t *Data)
Definition: ci.c:1883
#define AOT_CLEAR_REPLACE
Definition: ci.c:656
#define DATA_INDICATOR
Definition: ci.c:457
virtual const int * GetCaSystemIds(void)
Definition: ci.c:2570
uint8_t Status(void)
Definition: ci.c:566
void SetChecked(int CamSlotNumber)
Definition: ci.c:2835
#define CPCI_OK_DESCRAMBLING
Definition: ci.c:895
void Dump(int SlotNumber, bool Outgoing)
Definition: ci.c:537
#define T_TC_ERROR
Definition: ci.c:467
int CardIndex(void) const
Returns the card index of this device (0 ... MAXDEVICES - 1).
Definition: device.h:214
cCiResourceHandlers(void)
Creates the default list of resourceIds.
Definition: ci.c:1740
bool AddPid(int Pid)
Adds the given Pid to the list of PIDs of this receiver.
Definition: receiver.c:42
#define AOT_MENU_ANSW
Definition: ci.c:671
const cCiResourceHandler * Prev(const cCiResourceHandler *Object) const
Definition: tools.h:610
cCamSlot * MasterSlot(void)
Returns this CAM slot&#39;s master slot, or a pointer to itself if it is a master slot.
Definition: ci.h:308
int PutCat(const uchar *Data, int Count)
Definition: mtd.c:353
const cCamResponse * First(void) const
Returns the first element in this list, or NULL if the list is empty.
Definition: tools.h:606
#define RI_DATE_TIME
Definition: ci.c:638
static uint8_t * SetLength(uint8_t *Data, int Length)
Definition: ci.c:57
virtual bool Reset(void)
Resets the CAM in this slot.
Definition: ci.c:2335
time_t lastTime
Definition: ci.c:1268
void SendCaPmts(cCiCaPmtList &CaPmtList)
Sends the given list of CA_PMTs to the CAM.
Definition: ci.c:2524
bool CamResponsesLoad(const char *FileName, bool AllowComments, bool MustExist)
Definition: ci.c:447
virtual bool IsDecrypting(void)
Returns true if the CAM in this slot is currently used for decrypting.
Definition: ci.c:2712
bool createConnectionRequested
Definition: ci.c:583
#define SS_OK
Definition: ci.c:629
int transponder
Definition: ci.h:253
cCiMMI(uint16_t SessionId, cCiTransportConnection *Tc)
Definition: ci.c:1386
void SendMenuAnswer(uint8_t Selection)
Definition: ci.c:1571
void SendData(int Length, const uint8_t *Data)
Definition: ci.c:1824
const char * GetCamName(void)
Definition: ci.c:1811
int Vpid(void) const
Definition: channels.h:154
#define T_RCV
Definition: ci.c:460
void DelPid(int Pid)
Deletes the given Pid from the list of PIDs of this receiver.
Definition: receiver.c:90
void ClrDecrypt(tChannelID ChannelID, int CamSlotNumber)
Definition: ci.c:2952
void Del(cCiCaPmt *CaPmt)
Definition: ci.c:2474
virtual void SetPid(int Pid, bool Active)
Sets the given Pid (which has previously been added through a call to AddPid()) to Active...
Definition: ci.c:2618
bool HasCaPids(void) const
Definition: ci.c:133
cCamSlot * camSlots[MAX_CAM_SLOTS_PER_ADAPTER]
Definition: ci.h:175
virtual cCiEnquiry * GetEnquiry(void)
Gets a pending enquiry, or NULL if there is no enquiry.
Definition: ci.c:2448
cChannelCamRelation * AddEntry(tChannelID ChannelID)
Definition: ci.c:2896
cString fileName
Definition: ci.h:512
cCiDateTime(uint16_t SessionId, cCiTransportConnection *Tc)
Definition: ci.c:1275
void SetTsPostProcessor(cCiSession *CiSession)
Definition: ci.c:1793
cMutex mutex
Definition: ci.h:511
#define AOT_DATE_TIME_ENQ
Definition: ci.c:658
uchar Get(int Index)
Definition: tools.h:840
#define TC_ALIVE_TIMEOUT
Definition: ci.c:1770
uint8_t * Buffer(void)
Definition: ci.c:484
#define T_NEW_TC
Definition: ci.c:466
time_t lastChecked
Definition: ci.c:2795
bool WaitForAllCamSlotsReady(int Timeout=0)
Waits until all CAM slots have become ready, or the given Timeout (seconds) has expired.
Definition: ci.c:2767
cCiMenu * fetchedMenu
Definition: ci.c:1372
#define CAM_CHECKED_TIMEOUT
Definition: ci.c:2788
bool modified
Definition: ci.c:2056
virtual ~cCiMMI()
Definition: ci.c:1394
void Del(cListObject *Object, bool DeleteObject=true)
Definition: tools.c:2184
cMtdHandler * mtdHandler
Definition: ci.h:256
~cCiEnquiry()
Definition: ci.c:1664
bool TsPostProcess(uint8_t *TsPacket)
Definition: ci.c:1798
#define RI_RESOURCE_MANAGER
Definition: ci.c:634
int DeviceNumber(void) const
Returns the number of this device (0 ... numDevices - 1).
Definition: device.c:160
bool IsDecrypting(void)
Returns true if any of the active MTD CAM slots is currently decrypting.
Definition: mtd.c:96
virtual void Process(int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:763
bool Valid(void) const
Definition: channels.h:60
#define DRI_MMI_MODE_ACK
Definition: ci.c:1351
int Put(const uchar *Data, int Count)
Puts at most Count bytes of Data into the CAM slot which&#39;s index is derived from the PID of the TS pa...
Definition: mtd.c:60
void SendPMT(cCiCaPmt *CaPmt)
Definition: ci.c:1221
void Reply(const char *s)
Definition: ci.c:1672
void MtdMapSid(uchar *p, cMtdMapper *MtdMapper)
Definition: mtd.c:225
#define T_DTC_REPLY
Definition: ci.c:464
cCiMenu * Menu(bool Clear=false)
Definition: ci.c:1549
int NumIds(void)
Definition: ci.h:111
~cCamResponse()
Definition: ci.c:349
Definition: ci.h:119
uchar * bufp
Definition: ci.c:122
#define SS_NOT_ALLOCATED
Definition: ci.c:630
virtual eModuleStatus ModuleStatus(int Slot)
Returns the status of the CAM in the given Slot.
Definition: ci.h:197
virtual bool HasMMI(void)
Returns &#39;true&#39; if the CAM in this slot has an active MMI.
Definition: ci.c:2417
cDevice * assignedDevice
Definition: ci.h:242
virtual ~cCaActivationReceiver()
Definition: ci.c:297
cCaActivationReceiver(const cChannel *Channel, cCamSlot *CamSlot)
Definition: ci.c:289
#define MODULE_CHECK_INTERVAL
Definition: ci.c:2135
void Cancel(void)
Definition: ci.c:1679
tChannelID ChannelID(void)
Definition: ci.c:2799
int Length(void)
Definition: remux.h:449
void SetChecked(tChannelID ChannelID, int CamSlotNumber)
Definition: ci.c:2928
#define T_DATA_LAST
Definition: ci.c:468
uint8_t cmdId
Definition: ci.c:903
virtual const char * GetCamName(void)
Returns the name of the CAM in this slot, or NULL if there is no ready CAM in this slot...
Definition: ci.c:2405
#define tr(s)
Definition: i18n.h:85
char * entries[MAX_CIMENU_ENTRIES]
Definition: ci.h:130
bool TimedOut(void)
Definition: ci.c:2816
cCiEnquiry * fetchedEnquiry
Definition: ci.c:1373
void ClrDecrypt(int CamSlotNumber)
Definition: ci.c:2854
cCiResourceHandler(void)
Creates a new resource handler, through which the available resources can be provides.
Definition: ci.c:1693
bool TsIsScrambled(const uchar *p)
Definition: remux.h:98
cChannelCamRelations ChannelCamRelations
Definition: ci.c:2864
cListObject * Next(void) const
Definition: tools.h:510
virtual bool Ready(void)
Returns &#39;true&#39; if the CAM in this slot is ready to decrypt.
Definition: ci.c:2411
cCamResponse(void)
Definition: ci.c:342
void DeleteConnection(void)
Definition: ci.c:606
char * skipspace(const char *s)
Definition: tools.h:209
cTimeMs moduleCheckTimer
Definition: ci.h:250
cCaPidReceiver * caPidReceiver
Definition: ci.h:243
cCamSlot * camSlot
Definition: ci.c:279
#define T_DELETE_TC
Definition: ci.c:463
time_t lastCleanup
Definition: ci.h:515
cMutex * mutex
Definition: ci.h:125
virtual void CancelActivation(void)
Cancels a previously started activation (if any).
Definition: ci.c:2373
#define isyslog(a...)
Definition: tools.h:36
virtual uchar * Decrypt(uchar *Data, int &Count)
If this is a CAM slot that can be freely assigned to any device, but will not be directly inserted in...
Definition: ci.c:2730
virtual bool CanDecrypt(const cChannel *Channel, cMtdMapper *MtdMapper=NULL)
Returns true if there is a CAM in this slot that is able to decrypt the given Channel (or at least cl...
Definition: ci.c:2661
Definition: thread.h:79
Definition: ci.c:900
bool mtdAvailable
Definition: ci.h:255
int Priority(void)
Returns the priority of the device this slot is currently assigned to, or IDLEPRIORITY if it is not a...
Definition: ci.c:2577
bool CamChecked(tChannelID ChannelID, int CamSlotNumber)
Definition: ci.c:2914
virtual void StopDecrypting(void)
Clears the list of CA_PMT entries and tells the CAM to stop decrypting.
Definition: ci.c:2702
void Append(const uchar *Data, int Length)
Definition: tools.c:2328
virtual bool TsPostProcess(uint8_t *TsPacket)
If this cCiSession needs to do additional processing on TS packets (after the CAM has done the decryp...
Definition: ci.h:57
virtual ~cCiSession()
Definition: ci.c:701
void ClrChecked(tChannelID ChannelID, int CamSlotNumber)
Definition: ci.c:2944
#define TS_PACKET_FACTOR
Definition: ci.c:275
void AddCaDescriptors(int Length, const uint8_t *Data)
Definition: ci.c:963
const uint8_t * GetData(const uint8_t *Data, int &Length)
Definition: ci.c:730
void put_unaligned(unsigned int v, T *p)
Definition: tools.h:86
Definition: tools.h:369
void Abort(void)
Definition: ci.c:1684
#define TS_SIZE
Definition: remux.h:34
void Reset(int CamSlotNumber)
Definition: ci.c:2905
#define T_DATA_MORE
Definition: ci.c:469
cCiTransportConnection(cCamSlot *CamSlot, uint8_t Tcid)
Definition: ci.c:1772
virtual bool EnterMenu(void)
Requests the CAM in this slot to start its menu.
Definition: ci.c:2428
eModuleStatus
Definition: ci.h:170
int GetCaPids(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, int *Pids)
Gets all CA pids for a given channel.
Definition: pat.c:273
void Save(void)
Definition: ci.c:2994
cCiMenu(cCiMMI *MMI, bool Selectable)
Definition: ci.c:1598
void Register(cCiResourceHandler *ResourceHandler)
Adds the given ResourceHandler to the list of resource handlers and appends its ResourceIds to the gl...
Definition: ci.c:1745
tChannelID channelID
Definition: ci.c:2792
#define TC_POLL_TIMEOUT
Definition: ci.c:1769
#define ST_CREATE_SESSION_RESPONSE
Definition: ci.c:623
#define QUERY_REPLY_TIMEOUT
Definition: ci.c:1072
cCamSlot(cCiAdapter *CiAdapter, bool WantsTsData=false, cCamSlot *MasterSlot=NULL)
Creates a new CAM slot for the given CiAdapter.
Definition: ci.c:2138
static const uint8_t * GetLength(const uint8_t *Data, int &Length)
Definition: ci.c:42
void UnAssignAll(void)
Unassigns all MTD CAM slots from their devices.
Definition: mtd.c:137
cCiMMI * mmi
Definition: ci.h:124
uchar * Data(void)
Definition: tools.h:842
char * text
Definition: ci.c:333
#define DEC2BCD(d)
#define AOT_DATE_TIME
Definition: ci.c:659
bool deleteConnectionRequested
Definition: ci.c:584
void CloseSession(uint16_t SessionId)
Definition: ci.c:1906
virtual bool Assign(cDevice *Device, bool Query=false)
Assigns this adapter to the given Device, if this is possible.
Definition: ci.h:199
uint8_t buffer[MAX_TPDU_SIZE]
Definition: ci.c:474
void Abort(void)
Definition: ci.c:1646
cChannelCamRelation * GetEntry(tChannelID ChannelID)
Definition: ci.c:2885
#define RI_APPLICATION_INFORMATION
Definition: ci.c:635
virtual void Clear(void)
Definition: tools.h:768
virtual bool Assign(cDevice *Device, bool Query=false)
Assigns this CAM slot to the given Device, if this is possible.
Definition: ci.c:2181
virtual const uint32_t * ResourceIds(void) const
Returns a pointer to an array of resource identifiers, where the last value is zero.
Definition: ci.c:1709
#define AOT_CA_PMT
Definition: ci.c:652
bool IsActivating(void)
Returns true if any of the active MTD CAM slots is currently activating.
Definition: mtd.c:121
uchar buffer[2048]
Definition: ci.c:121
Definition: ci.h:170
virtual bool IsActivating(void)
Returns true if this CAM slot is currently activating a smart card.
Definition: ci.c:2384
void SendTPDU(uint8_t Tag, int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:1817
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting &#39;running&#39; to false, so that the Action() loop can finish in an or...
Definition: thread.c:354
cMtdMapper * MtdMapper(void)
Definition: mtd.h:168
int NumPids(void) const
Definition: receiver.h:81
void Poll(void)
Definition: ci.c:1849
char * text
Definition: ci.h:154
~cCiCaPmtList()
Definition: ci.c:2461
cCamSlot * CamSlot(void)
Definition: ci.c:603
int Count(void) const
Definition: tools.h:590
cCiCaPmt * Add(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds)
Definition: ci.c:2467
#define QUERY_RETRIES
Definition: ci.c:1073
#define UNSCRAMBLE_TIME
Definition: ci.c:274
bool ReceivedReply(void)
Definition: ci.c:1090
cMutex * mutex
Definition: ci.h:153
cChannelCamRelations(void)
Definition: ci.c:2866
void DeleteAllConnections(void)
Definition: ci.c:2245
void Detach(void)
Definition: receiver.c:125
void SetResourceId(uint32_t Id)
If this is a class that has been derived from an existing cCiSession class, but implements a differen...
Definition: ci.c:705
bool RepliesToQuery(void)
Definition: ci.c:1088
virtual void Process(int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:837
int transponder
Definition: ci.c:908
const uint8_t * Data(int &Length)
Definition: ci.c:482
int Priority(void)
Returns the maximum priority of any of the active MTD CAM slots.
Definition: mtd.c:88
static int MtdMapStreams(uchar *p, cMtdMapper *MtdMapper, int Length)
Definition: ci.c:1023
int MaxSize(void)
Definition: ci.c:487
#define AOT_CA_PMT_REPLY
Definition: ci.c:653
cCamSlots CamSlots
Definition: ci.c:2755
int Priority(void) const
Returns the priority of the current receiving session (-MAXPRIORITY..MAXPRIORITY), or IDLEPRIORITY if no receiver is currently active.
Definition: device.c:1630
int SlotNumber(void)
Returns the number of this CAM slot within the whole system.
Definition: ci.h:343
#define CAEI_POSSIBLE
Definition: ci.c:1061
int interval
Definition: ci.c:1267
uchar mtdCatBuffer[TS_SIZE]
Definition: ci.c:123
Definition: tools.h:176
virtual ~cCiApplicationInformation()
Definition: ci.c:832
static bool DumpPolls
Definition: ci.c:33
static bool DebugProtocol
Definition: ci.c:32
bool selectable
Definition: ci.h:126
cCiResourceManager(uint16_t SessionId, cCiTransportConnection *Tc)
Definition: ci.c:777
bool AddEntry(char *s)
Definition: ci.c:1619
virtual const uint32_t * ResourceIds(void) const =0
Returns a pointer to an array of resource identifiers, where the last value is zero.
int catVersion
Definition: ci.c:119
Definition: ci.h:170
cSkins Skins
Definition: skins.c:219
cDevice * Device(void)
Returns the device this CAM slot is currently assigned to.
Definition: ci.h:331
bool TimedOut(void) const
Definition: tools.c:779
virtual bool RepliesToQuery(void)
Returns true if the CAM in this slot replies to queries and thus supports MCD ("Multi Channel Decrypt...
Definition: ci.c:2480
#define CPCI_QUERY
Definition: ci.c:897
void SetSize(int Size)
Definition: ci.c:486
const cCamResponse * Next(const cCamResponse *Object) const
< Returns the element immediately before Object in this list, or NULL if Object is the first element ...
Definition: tools.h:613
void SetDecrypt(int CamSlotNumber)
Definition: ci.c:2842