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