vdr  2.2.0
ci.c
Go to the documentation of this file.
1 /*
2  * ci.c: Common Interface
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * $Id: ci.c 3.19 2015/02/02 13:57:39 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 <string.h>
17 #include <sys/ioctl.h>
18 #include <time.h>
19 #include <unistd.h>
20 #include "device.h"
21 #include "pat.h"
22 #include "receiver.h"
23 #include "remux.h"
24 #include "libsi/si.h"
25 #include "skins.h"
26 #include "tools.h"
27 
28 // Set these to 'true' for debug output:
29 static bool DumpTPDUDataTransfer = false;
30 static bool DebugProtocol = false;
31 static bool DumpPolls = false;
32 static bool DumpDateTime = false;
33 
34 #define dbgprotocol(a...) do { if (DebugProtocol) fprintf(stderr, a); } while (0)
35 
36 // --- Helper functions ------------------------------------------------------
37 
38 #define SIZE_INDICATOR 0x80
39 
40 static const uint8_t *GetLength(const uint8_t *Data, int &Length)
44 {
45  Length = *Data++;
46  if ((Length & SIZE_INDICATOR) != 0) {
47  int l = Length & ~SIZE_INDICATOR;
48  Length = 0;
49  for (int i = 0; i < l; i++)
50  Length = (Length << 8) | *Data++;
51  }
52  return Data;
53 }
54 
55 static uint8_t *SetLength(uint8_t *Data, int Length)
58 {
59  uint8_t *p = Data;
60  if (Length < 128)
61  *p++ = Length;
62  else {
63  int n = sizeof(Length);
64  for (int i = n - 1; i >= 0; i--) {
65  int b = (Length >> (8 * i)) & 0xFF;
66  if (p != Data || b)
67  *++p = b;
68  }
69  *Data = (p - Data) | SIZE_INDICATOR;
70  p++;
71  }
72  return p;
73 }
74 
75 static char *CopyString(int Length, const uint8_t *Data)
78 {
79  // Some CAMs send funny characters at the beginning of strings.
80  // Let's just skip them:
81  while (Length > 0 && (*Data == ' ' || *Data == 0x05 || *Data == 0x96 || *Data == 0x97)) {
82  Length--;
83  Data++;
84  }
85  char *s = MALLOC(char, Length + 1);
86  strncpy(s, (char *)Data, Length);
87  s[Length] = 0;
88  // The character 0x8A is used as newline, so let's put a real '\n' in there:
89  strreplace(s, 0x8A, '\n');
90  return s;
91 }
92 
93 static char *GetString(int &Length, const uint8_t **Data)
97 {
98  if (Length > 0 && Data && *Data) {
99  int l = 0;
100  const uint8_t *d = GetLength(*Data, l);
101  char *s = CopyString(l, d);
102  Length -= d - *Data + l;
103  *Data = d + l;
104  return s;
105  }
106  return NULL;
107 }
108 
109 // --- cCaPidReceiver --------------------------------------------------------
110 
111 // A receiver that is used to make the device receive the ECM pids, as well as the
112 // CAT and the EMM pids.
113 
114 class cCaPidReceiver : public cReceiver {
115 private:
118  uchar buffer[2048]; // 11 bit length, max. 2048 byte
120  int length;
121  void AddEmmPid(int Pid);
122  void DelEmmPids(void);
123 protected:
124  virtual void Activate(bool On);
125 public:
126  cCaPidReceiver(void);
127  virtual ~cCaPidReceiver() { Detach(); }
128  virtual void Receive(uchar *Data, int Length);
129  bool HasCaPids(void) { return NumPids() - emmPids.Size() - 1 > 0; }
130  void Reset(void) { DelEmmPids(); catVersion = -1; }
131  };
132 
134 {
135  catVersion = -1;
136  bufp = NULL;
137  length = 0;
138  AddPid(CATPID);
139 }
140 
142 {
143  for (int i = 0; i < emmPids.Size(); i++) {
144  if (emmPids[i] == Pid)
145  return;
146  }
147  emmPids.Append(Pid);
148  AddPid(Pid);
149 }
150 
152 {
153  for (int i = 0; i < emmPids.Size(); i++)
154  DelPid(emmPids[i]);
155  emmPids.Clear();
156 }
157 
159 {
160  catVersion = -1; // can be done independent of 'On'
161 }
162 
163 void cCaPidReceiver::Receive(uchar *Data, int Length)
164 {
165  if (TsPid(Data) == CATPID) {
166  uchar *p = NULL;
167  if (TsPayloadStart(Data)) {
168  if (Data[5] == SI::TableIdCAT) {
169  length = (int(Data[6] & 0x03) << 8) | Data[7]; // section length
170  if (length > 5) {
171  int v = (Data[10] & 0x3E) >> 1; // version number
172  if (v != catVersion) {
173  if (Data[11] == 0 && Data[12] == 0) { // section number, last section number
174  if (length > TS_SIZE - 8) {
175  int n = TS_SIZE - 13;
176  memcpy(buffer, Data + 13, n);
177  bufp = buffer + n;
178  length -= n + 5; // 5 = header
179  }
180  else {
181  p = Data + 13; // no need to copy the data
182  length -= 5; // header
183  }
184  }
185  else
186  dsyslog("multi table CAT section - unhandled!");
187  catVersion = v;
188  }
189  }
190  }
191  }
192  else if (bufp && length > 0) {
193  int n = min(length, TS_SIZE - 4);
194  if (bufp + n - buffer <= int(sizeof(buffer))) {
195  memcpy(bufp, Data + 4, n);
196  bufp += n;
197  length -= n;
198  if (length <= 0) {
199  p = buffer;
200  length = bufp - buffer;
201  }
202  }
203  else {
204  esyslog("ERROR: buffer overflow in cCaPidReceiver::Receive()");
205  bufp = 0;
206  length = 0;
207  }
208  }
209  if (p) {
210  int OldCatVersion = catVersion; // must preserve the current version number
211  cDevice *AttachedDevice = Device();
212  if (AttachedDevice)
213  AttachedDevice->Detach(this);
214  DelEmmPids();
215  for (int i = 0; i < length - 4; i++) { // -4 = checksum
216  if (p[i] == 0x09) {
217  int CaId = int(p[i + 2] << 8) | p[i + 3];
218  int EmmPid = int(((p[i + 4] & 0x1F) << 8)) | p[i + 5];
219  AddEmmPid(EmmPid);
220  switch (CaId >> 8) {
221  case 0x01: for (int j = i + 7; j < p[i + 1] + 2; j += 4) {
222  EmmPid = (int(p[j] & 0x0F) << 8) | p[j + 1];
223  AddEmmPid(EmmPid);
224  }
225  break;
226  }
227  i += p[i + 1] + 2 - 1; // -1 to compensate for the loop increment
228  }
229  }
230  if (AttachedDevice)
231  AttachedDevice->AttachReceiver(this);
232  catVersion = OldCatVersion;
233  p = NULL;
234  bufp = 0;
235  length = 0;
236  }
237  }
238 }
239 
240 // --- cCaActivationReceiver -------------------------------------------------
241 
242 // A receiver that is used to make the device stay on a given channel and
243 // keep the CAM slot assigned.
244 
245 #define UNSCRAMBLE_TIME 5 // seconds of receiving purely unscrambled data before considering the smart card "activated"
246 #define TS_PACKET_FACTOR 1024 // only process every TS_PACKET_FACTORth packet to keep the load down
247 
249 private:
253 protected:
254  virtual void Receive(uchar *Data, int Length);
255 public:
256  cCaActivationReceiver(const cChannel *Channel, cCamSlot *CamSlot);
257  virtual ~cCaActivationReceiver();
258  };
259 
261 :cReceiver(Channel, MINPRIORITY + 1)
262 {
263  camSlot = CamSlot;
264  lastScrambledTime = time(NULL);
265  numTsPackets = 0;
266 }
267 
269 {
270  Detach();
271 }
272 
273 void cCaActivationReceiver::Receive(uchar *Data, int Length)
274 {
275  if (numTsPackets++ % TS_PACKET_FACTOR == 0) {
276  time_t Now = time(NULL);
277  if (TsIsScrambled(Data))
278  lastScrambledTime = Now;
279  else if (Now - lastScrambledTime > UNSCRAMBLE_TIME) {
280  dsyslog("CAM %d: activated!", camSlot->SlotNumber());
281  Skins.QueueMessage(mtInfo, tr("CAM activated!"));
282  Detach();
283  }
284  }
285 }
286 
287 // --- cTPDU -----------------------------------------------------------------
288 
289 #define MAX_TPDU_SIZE 2048
290 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
291 
292 #define DATA_INDICATOR 0x80
293 
294 #define T_SB 0x80
295 #define T_RCV 0x81
296 #define T_CREATE_TC 0x82
297 #define T_CTC_REPLY 0x83
298 #define T_DELETE_TC 0x84
299 #define T_DTC_REPLY 0x85
300 #define T_REQUEST_TC 0x86
301 #define T_NEW_TC 0x87
302 #define T_TC_ERROR 0x88
303 #define T_DATA_LAST 0xA0
304 #define T_DATA_MORE 0xA1
305 
306 class cTPDU {
307 private:
308  int size;
309  uint8_t buffer[MAX_TPDU_SIZE];
310  const uint8_t *GetData(const uint8_t *Data, int &Length);
311 public:
312  cTPDU(void) { size = 0; }
313  cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length = 0, const uint8_t *Data = NULL);
314  uint8_t Slot(void) { return buffer[0]; }
315  uint8_t Tcid(void) { return buffer[1]; }
316  uint8_t Tag(void) { return buffer[2]; }
317  const uint8_t *Data(int &Length) { return GetData(buffer + 3, Length); }
318  uint8_t Status(void);
319  uint8_t *Buffer(void) { return buffer; }
320  int Size(void) { return size; }
321  void SetSize(int Size) { size = Size; }
322  int MaxSize(void) { return sizeof(buffer); }
323  void Dump(int SlotNumber, bool Outgoing);
324  };
325 
326 cTPDU::cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length, const uint8_t *Data)
327 {
328  size = 0;
329  buffer[0] = Slot;
330  buffer[1] = Tcid;
331  buffer[2] = Tag;
332  switch (Tag) {
333  case T_RCV:
334  case T_CREATE_TC:
335  case T_CTC_REPLY:
336  case T_DELETE_TC:
337  case T_DTC_REPLY:
338  case T_REQUEST_TC:
339  buffer[3] = 1; // length
340  buffer[4] = Tcid;
341  size = 5;
342  break;
343  case T_NEW_TC:
344  case T_TC_ERROR:
345  if (Length == 1) {
346  buffer[3] = 2; // length
347  buffer[4] = Tcid;
348  buffer[5] = Data[0];
349  size = 6;
350  }
351  else
352  esyslog("ERROR: invalid data length for TPDU tag 0x%02X: %d (%d/%d)", Tag, Length, Slot, Tcid);
353  break;
354  case T_DATA_LAST:
355  case T_DATA_MORE:
356  if (Length <= MAX_TPDU_DATA) {
357  uint8_t *p = buffer + 3;
358  p = SetLength(p, Length + 1);
359  *p++ = Tcid;
360  if (Length)
361  memcpy(p, Data, Length);
362  size = Length + (p - buffer);
363  }
364  else
365  esyslog("ERROR: invalid data length for TPDU tag 0x%02X: %d (%d/%d)", Tag, Length, Slot, Tcid);
366  break;
367  default:
368  esyslog("ERROR: unknown TPDU tag: 0x%02X (%d/%d)", Tag, Slot, Tcid);
369  }
370  }
371 
372 void cTPDU::Dump(int SlotNumber, bool Outgoing)
373 {
374  if (DumpTPDUDataTransfer && (DumpPolls || Tag() != T_SB)) {
375 #define MAX_DUMP 256
376  fprintf(stderr, " %d: %s ", SlotNumber, Outgoing ? "-->" : "<--");
377  for (int i = 0; i < size && i < MAX_DUMP; i++)
378  fprintf(stderr, "%02X ", buffer[i]);
379  fprintf(stderr, "%s\n", size >= MAX_DUMP ? "..." : "");
380  if (!Outgoing) {
381  fprintf(stderr, " ");
382  for (int i = 0; i < size && i < MAX_DUMP; i++)
383  fprintf(stderr, "%2c ", isprint(buffer[i]) ? buffer[i] : '.');
384  fprintf(stderr, "%s\n", size >= MAX_DUMP ? "..." : "");
385  }
386  }
387 }
388 
389 const uint8_t *cTPDU::GetData(const uint8_t *Data, int &Length)
390 {
391  if (size) {
392  Data = GetLength(Data, Length);
393  if (Length) {
394  Length--; // the first byte is always the tcid
395  return Data + 1;
396  }
397  }
398  return NULL;
399 }
400 
401 uint8_t cTPDU::Status(void)
402 {
403  if (size >= 4 && buffer[size - 4] == T_SB && buffer[size - 3] == 2)
404  return buffer[size - 1];
405  return 0;
406 }
407 
408 // --- cCiTransportConnection ------------------------------------------------
409 
410 #define MAX_SESSIONS_PER_TC 16
411 
413 private:
414  enum eState { stIDLE, stCREATION, stACTIVE, stDELETION };
416  uint8_t tcid;
420  bool hasUserIO;
423  cCiSession *sessions[MAX_SESSIONS_PER_TC + 1]; // session numbering starts with 1
424  void SendTPDU(uint8_t Tag, int Length = 0, const uint8_t *Data = NULL);
425  void SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId = 0, int Status = -1);
426  void Poll(void);
427  uint32_t ResourceIdToInt(const uint8_t *Data);
428  cCiSession *GetSessionBySessionId(uint16_t SessionId);
429  void OpenSession(int Length, const uint8_t *Data);
430  void CloseSession(uint16_t SessionId);
431  void HandleSessions(cTPDU *TPDU);
432 public:
433  cCiTransportConnection(cCamSlot *CamSlot, uint8_t Tcid);
434  virtual ~cCiTransportConnection();
435  cCamSlot *CamSlot(void) { return camSlot; }
436  uint8_t Tcid(void) const { return tcid; }
437  void CreateConnection(void) { createConnectionRequested = true; }
438  void DeleteConnection(void) { deleteConnectionRequested = true; }
439  const char *GetCamName(void);
440  bool Ready(void);
441  bool HasUserIO(void) { return hasUserIO; }
442  void SendData(int Length, const uint8_t *Data);
443  bool Process(cTPDU *TPDU = NULL);
444  cCiSession *GetSessionByResourceId(uint32_t ResourceId);
445  };
446 
447 // --- cCiSession ------------------------------------------------------------
448 
449 // Session Tags:
450 
451 #define ST_SESSION_NUMBER 0x90
452 #define ST_OPEN_SESSION_REQUEST 0x91
453 #define ST_OPEN_SESSION_RESPONSE 0x92
454 #define ST_CREATE_SESSION 0x93
455 #define ST_CREATE_SESSION_RESPONSE 0x94
456 #define ST_CLOSE_SESSION_REQUEST 0x95
457 #define ST_CLOSE_SESSION_RESPONSE 0x96
458 
459 // Session Status:
460 
461 #define SS_OK 0x00
462 #define SS_NOT_ALLOCATED 0xF0
463 
464 // Resource Identifiers:
465 
466 #define RI_RESOURCE_MANAGER 0x00010041
467 #define RI_APPLICATION_INFORMATION 0x00020041
468 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
469 #define RI_HOST_CONTROL 0x00200041
470 #define RI_DATE_TIME 0x00240041
471 #define RI_MMI 0x00400041
472 
473 // Application Object Tags:
474 
475 #define AOT_NONE 0x000000
476 #define AOT_PROFILE_ENQ 0x9F8010
477 #define AOT_PROFILE 0x9F8011
478 #define AOT_PROFILE_CHANGE 0x9F8012
479 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
480 #define AOT_APPLICATION_INFO 0x9F8021
481 #define AOT_ENTER_MENU 0x9F8022
482 #define AOT_CA_INFO_ENQ 0x9F8030
483 #define AOT_CA_INFO 0x9F8031
484 #define AOT_CA_PMT 0x9F8032
485 #define AOT_CA_PMT_REPLY 0x9F8033
486 #define AOT_TUNE 0x9F8400
487 #define AOT_REPLACE 0x9F8401
488 #define AOT_CLEAR_REPLACE 0x9F8402
489 #define AOT_ASK_RELEASE 0x9F8403
490 #define AOT_DATE_TIME_ENQ 0x9F8440
491 #define AOT_DATE_TIME 0x9F8441
492 #define AOT_CLOSE_MMI 0x9F8800
493 #define AOT_DISPLAY_CONTROL 0x9F8801
494 #define AOT_DISPLAY_REPLY 0x9F8802
495 #define AOT_TEXT_LAST 0x9F8803
496 #define AOT_TEXT_MORE 0x9F8804
497 #define AOT_KEYPAD_CONTROL 0x9F8805
498 #define AOT_KEYPRESS 0x9F8806
499 #define AOT_ENQ 0x9F8807
500 #define AOT_ANSW 0x9F8808
501 #define AOT_MENU_LAST 0x9F8809
502 #define AOT_MENU_MORE 0x9F880A
503 #define AOT_MENU_ANSW 0x9F880B
504 #define AOT_LIST_LAST 0x9F880C
505 #define AOT_LIST_MORE 0x9F880D
506 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
507 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
508 #define AOT_DISPLAY_MESSAGE 0x9F8810
509 #define AOT_SCENE_END_MARK 0x9F8811
510 #define AOT_SCENE_DONE 0x9F8812
511 #define AOT_SCENE_CONTROL 0x9F8813
512 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
513 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
514 #define AOT_FLUSH_DOWNLOAD 0x9F8816
515 #define AOT_DOWNLOAD_REPLY 0x9F8817
516 #define AOT_COMMS_CMD 0x9F8C00
517 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
518 #define AOT_COMMS_REPLY 0x9F8C02
519 #define AOT_COMMS_SEND_LAST 0x9F8C03
520 #define AOT_COMMS_SEND_MORE 0x9F8C04
521 #define AOT_COMMS_RCV_LAST 0x9F8C05
522 #define AOT_COMMS_RCV_MORE 0x9F8C06
523 
524 class cCiSession {
525 private:
526  uint16_t sessionId;
527  uint32_t resourceId;
529 protected:
530  int GetTag(int &Length, const uint8_t **Data);
531  const uint8_t *GetData(const uint8_t *Data, int &Length);
532  void SendData(int Tag, int Length = 0, const uint8_t *Data = NULL);
533  cCiTransportConnection *Tc(void) { return tc; }
534 public:
535  cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc);
536  virtual ~cCiSession();
537  uint16_t SessionId(void) { return sessionId; }
538  uint32_t ResourceId(void) { return resourceId; }
539  virtual bool HasUserIO(void) { return false; }
540  virtual void Process(int Length = 0, const uint8_t *Data = NULL);
541  };
542 
543 cCiSession::cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc)
544 {
545  sessionId = SessionId;
546  resourceId = ResourceId;
547  tc = Tc;
548 }
549 
551 {
552 }
553 
554 int cCiSession::GetTag(int &Length, const uint8_t **Data)
558 {
559  if (Length >= 3 && Data && *Data) {
560  int t = 0;
561  for (int i = 0; i < 3; i++)
562  t = (t << 8) | *(*Data)++;
563  Length -= 3;
564  return t;
565  }
566  return AOT_NONE;
567 }
568 
569 const uint8_t *cCiSession::GetData(const uint8_t *Data, int &Length)
570 {
571  Data = GetLength(Data, Length);
572  return Length ? Data : NULL;
573 }
574 
575 void cCiSession::SendData(int Tag, int Length, const uint8_t *Data)
576 {
577  uint8_t buffer[2048];
578  uint8_t *p = buffer;
579  *p++ = ST_SESSION_NUMBER;
580  *p++ = 0x02;
581  *p++ = (sessionId >> 8) & 0xFF;
582  *p++ = sessionId & 0xFF;
583  *p++ = (Tag >> 16) & 0xFF;
584  *p++ = (Tag >> 8) & 0xFF;
585  *p++ = Tag & 0xFF;
586  p = SetLength(p, Length);
587  if (p - buffer + Length < int(sizeof(buffer))) {
588  if (Data)
589  memcpy(p, Data, Length);
590  p += Length;
591  tc->SendData(p - buffer, buffer);
592  }
593  else
594  esyslog("ERROR: CAM %d: data length (%d) exceeds buffer size", Tc()->CamSlot()->SlotNumber(), Length);
595 }
596 
597 void cCiSession::Process(int Length, const uint8_t *Data)
598 {
599 }
600 
601 // --- cCiResourceManager ----------------------------------------------------
602 
604 private:
605  int state;
606 public:
607  cCiResourceManager(uint16_t SessionId, cCiTransportConnection *Tc);
608  virtual void Process(int Length = 0, const uint8_t *Data = NULL);
609  };
610 
612 :cCiSession(SessionId, RI_RESOURCE_MANAGER, Tc)
613 {
614  dbgprotocol("Slot %d: new Resource Manager (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
615  state = 0;
616 }
617 
618 void cCiResourceManager::Process(int Length, const uint8_t *Data)
619 {
620  if (Data) {
621  int Tag = GetTag(Length, &Data);
622  switch (Tag) {
623  case AOT_PROFILE_ENQ: {
624  dbgprotocol("Slot %d: <== Profile Enquiry (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
625  uint32_t resources[] = { htonl(RI_RESOURCE_MANAGER),
628  htonl(RI_DATE_TIME),
629  htonl(RI_MMI)
630  };
631  dbgprotocol("Slot %d: ==> Profile (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
632  SendData(AOT_PROFILE, sizeof(resources), (uint8_t*)resources);
633  state = 3;
634  }
635  break;
636  case AOT_PROFILE: {
637  dbgprotocol("Slot %d: <== Profile (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
638  if (state == 1) {
639  int l = 0;
640  const uint8_t *d = GetData(Data, l);
641  if (l > 0 && d)
642  esyslog("ERROR: CAM %d: resource manager: unexpected data", Tc()->CamSlot()->SlotNumber());
643  dbgprotocol("Slot %d: ==> Profile Change (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
645  state = 2;
646  }
647  else {
648  esyslog("ERROR: CAM %d: resource manager: unexpected tag %06X in state %d", Tc()->CamSlot()->SlotNumber(), Tag, state);
649  }
650  }
651  break;
652  default: esyslog("ERROR: CAM %d: resource manager: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
653  }
654  }
655  else if (state == 0) {
656  dbgprotocol("Slot %d: ==> Profile Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
658  state = 1;
659  }
660 }
661 
662 // --- cCiApplicationInformation ---------------------------------------------
663 
665 private:
666  int state;
670  char *menuString;
671 public:
673  virtual ~cCiApplicationInformation();
674  virtual void Process(int Length = 0, const uint8_t *Data = NULL);
675  bool EnterMenu(void);
676  const char *GetMenuString(void) { return menuString; }
677  };
678 
680 :cCiSession(SessionId, RI_APPLICATION_INFORMATION, Tc)
681 {
682  dbgprotocol("Slot %d: new Application Information (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
683  state = 0;
684  menuString = NULL;
685 }
686 
688 {
689  free(menuString);
690 }
691 
692 void cCiApplicationInformation::Process(int Length, const uint8_t *Data)
693 {
694  if (Data) {
695  int Tag = GetTag(Length, &Data);
696  switch (Tag) {
697  case AOT_APPLICATION_INFO: {
698  dbgprotocol("Slot %d: <== Application Info (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
699  int l = 0;
700  const uint8_t *d = GetData(Data, l);
701  if ((l -= 1) < 0) break;
702  applicationType = *d++;
703  if ((l -= 2) < 0) break;
704  applicationManufacturer = ntohs(get_unaligned((uint16_t *)d));
705  d += 2;
706  if ((l -= 2) < 0) break;
707  manufacturerCode = ntohs(get_unaligned((uint16_t *)d));
708  d += 2;
709  free(menuString);
710  menuString = GetString(l, &d);
711  isyslog("CAM %d: %s, %02X, %04X, %04X", Tc()->CamSlot()->SlotNumber(), menuString, applicationType, applicationManufacturer, manufacturerCode);
712  state = 2;
713  }
714  break;
715  default: esyslog("ERROR: CAM %d: application information: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
716  }
717  }
718  else if (state == 0) {
719  dbgprotocol("Slot %d: ==> Application Info Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
721  state = 1;
722  }
723 }
724 
726 {
727  if (state == 2) {
728  dbgprotocol("Slot %d: ==> Enter Menu (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
730  return true;
731  }
732  return false;
733 }
734 
735 // --- cCiCaPmt --------------------------------------------------------------
736 
737 #define MAXCASYSTEMIDS 64
738 
739 // Ca Pmt List Management:
740 
741 #define CPLM_MORE 0x00
742 #define CPLM_FIRST 0x01
743 #define CPLM_LAST 0x02
744 #define CPLM_ONLY 0x03
745 #define CPLM_ADD 0x04
746 #define CPLM_UPDATE 0x05
747 
748 // Ca Pmt Cmd Ids:
749 
750 #define CPCI_OK_DESCRAMBLING 0x01
751 #define CPCI_OK_MMI 0x02
752 #define CPCI_QUERY 0x03
753 #define CPCI_NOT_SELECTED 0x04
754 
755 class cCiCaPmt {
757 private:
758  uint8_t cmdId;
759  int length;
761  uint8_t capmt[2048];
762  int source;
765  int caSystemIds[MAXCASYSTEMIDS + 1]; // list is zero terminated!
766  void AddCaDescriptors(int Length, const uint8_t *Data);
767 public:
768  cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds);
769  uint8_t CmdId(void) { return cmdId; }
770  void SetListManagement(uint8_t ListManagement);
771  uint8_t ListManagement(void) { return capmt[0]; }
772  void AddPid(int Pid, uint8_t StreamType);
773  };
774 
775 cCiCaPmt::cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds)
776 {
777  cmdId = CmdId;
778  source = Source;
779  transponder = Transponder;
780  programNumber = ProgramNumber;
781  int i = 0;
782  if (CaSystemIds) {
783  for (; CaSystemIds[i]; i++)
784  caSystemIds[i] = CaSystemIds[i];
785  }
786  caSystemIds[i] = 0;
787  uint8_t caDescriptors[512];
788  int caDescriptorsLength = GetCaDescriptors(source, transponder, programNumber, caSystemIds, sizeof(caDescriptors), caDescriptors, 0);
789  length = 0;
790  capmt[length++] = CPLM_ONLY;
791  capmt[length++] = (ProgramNumber >> 8) & 0xFF;
792  capmt[length++] = ProgramNumber & 0xFF;
793  capmt[length++] = 0x01; // version_number, current_next_indicator - apparently vn doesn't matter, but cni must be 1
794  esInfoLengthPos = length;
795  capmt[length++] = 0x00; // program_info_length H (at program level)
796  capmt[length++] = 0x00; // program_info_length L
797  AddCaDescriptors(caDescriptorsLength, caDescriptors);
798 }
799 
800 void cCiCaPmt::SetListManagement(uint8_t ListManagement)
801 {
802  capmt[0] = ListManagement;
803 }
804 
805 void cCiCaPmt::AddPid(int Pid, uint8_t StreamType)
806 {
807  if (Pid) {
808  uint8_t caDescriptors[512];
809  int caDescriptorsLength = GetCaDescriptors(source, transponder, programNumber, caSystemIds, sizeof(caDescriptors), caDescriptors, Pid);
810  //XXX buffer overflow check???
811  capmt[length++] = StreamType;
812  capmt[length++] = (Pid >> 8) & 0xFF;
813  capmt[length++] = Pid & 0xFF;
814  esInfoLengthPos = length;
815  capmt[length++] = 0x00; // ES_info_length H (at ES level)
816  capmt[length++] = 0x00; // ES_info_length L
817  AddCaDescriptors(caDescriptorsLength, caDescriptors);
818  }
819 }
820 
821 void cCiCaPmt::AddCaDescriptors(int Length, const uint8_t *Data)
822 {
823  if (esInfoLengthPos) {
824  if (length + Length < int(sizeof(capmt))) {
825  if (Length || cmdId == CPCI_QUERY) {
826  capmt[length++] = cmdId;
827  memcpy(capmt + length, Data, Length);
828  length += Length;
829  int l = length - esInfoLengthPos - 2;
830  capmt[esInfoLengthPos] = (l >> 8) & 0xFF;
831  capmt[esInfoLengthPos + 1] = l & 0xFF;
832  }
833  }
834  else
835  esyslog("ERROR: buffer overflow in CA descriptor");
836  esInfoLengthPos = 0;
837  }
838  else
839  esyslog("ERROR: adding CA descriptor without Pid!");
840 }
841 
842 // --- cCiConditionalAccessSupport -------------------------------------------
843 
844 // CA Enable Ids:
845 
846 #define CAEI_POSSIBLE 0x01
847 #define CAEI_POSSIBLE_COND_PURCHASE 0x02
848 #define CAEI_POSSIBLE_COND_TECHNICAL 0x03
849 #define CAEI_NOT_POSSIBLE_ENTITLEMENT 0x71
850 #define CAEI_NOT_POSSIBLE_TECHNICAL 0x73
851 
852 #define CA_ENABLE_FLAG 0x80
853 
854 #define CA_ENABLE(x) (((x) & CA_ENABLE_FLAG) ? (x) & ~CA_ENABLE_FLAG : 0)
855 
856 #define QUERY_WAIT_TIME 1000 // ms to wait before sending a query
857 #define QUERY_REPLY_TIMEOUT 2000 // ms to wait for a reply to a query
858 
860 private:
861  int state;
863  int caSystemIds[MAXCASYSTEMIDS + 1]; // list is zero terminated!
866 public:
868  virtual void Process(int Length = 0, const uint8_t *Data = NULL);
869  const int *GetCaSystemIds(void) { return caSystemIds; }
870  void SendPMT(cCiCaPmt *CaPmt);
871  bool RepliesToQuery(void) { return repliesToQuery; }
872  bool Ready(void) { return state >= 4; }
873  bool ReceivedReply(void) { return state >= 5; }
874  bool CanDecrypt(void) { return state == 6; }
875  };
876 
879 {
880  dbgprotocol("Slot %d: new Conditional Access Support (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
881  state = 0; // inactive
882  caSystemIds[numCaSystemIds = 0] = 0;
883  repliesToQuery = false;
884 }
885 
886 void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
887 {
888  if (Data) {
889  int Tag = GetTag(Length, &Data);
890  switch (Tag) {
891  case AOT_CA_INFO: {
892  dbgprotocol("Slot %d: <== Ca Info (%d)", Tc()->CamSlot()->SlotNumber(), SessionId());
893  cString Ids;
894  numCaSystemIds = 0;
895  int l = 0;
896  const uint8_t *d = GetData(Data, l);
897  while (l > 1) {
898  uint16_t id = ((uint16_t)(*d) << 8) | *(d + 1);
899  Ids = cString::sprintf("%s %04X", *Ids ? *Ids : "", id);
900  dbgprotocol(" %04X", id);
901  d += 2;
902  l -= 2;
904  caSystemIds[numCaSystemIds++] = id;
905  else {
906  esyslog("ERROR: CAM %d: too many CA system IDs!", Tc()->CamSlot()->SlotNumber());
907  break;
908  }
909  }
911  dbgprotocol("\n");
912  if (state == 1) {
913  timer.Set(QUERY_WAIT_TIME); // WORKAROUND: Alphacrypt 3.09 doesn't reply to QUERY immediately after reset
914  state = 2; // got ca info
915  }
916  dsyslog("CAM %d: system ids:%s", Tc()->CamSlot()->SlotNumber(), *Ids ? *Ids : " none");
917  }
918  break;
919  case AOT_CA_PMT_REPLY: {
920  dbgprotocol("Slot %d: <== Ca Pmt Reply (%d)", Tc()->CamSlot()->SlotNumber(), SessionId());
921  if (!repliesToQuery) {
922  dsyslog("CAM %d: replies to QUERY - multi channel decryption possible", Tc()->CamSlot()->SlotNumber());
923  repliesToQuery = true;
924  }
925  state = 5; // got ca pmt reply
926  int l = 0;
927  const uint8_t *d = GetData(Data, l);
928  if (l > 1) {
929  uint16_t pnr = ((uint16_t)(*d) << 8) | *(d + 1);
930  dbgprotocol(" %d", pnr);
931  d += 2;
932  l -= 2;
933  if (l > 0) {
934  dbgprotocol(" %02X", *d);
935  d += 1;
936  l -= 1;
937  if (l > 0) {
938  if (l % 3 == 0 && l > 1) {
939  // The EN50221 standard defines that the next byte is supposed
940  // to be the CA_enable value at programme level. However, there are
941  // CAMs (for instance the AlphaCrypt with firmware <= 3.05) that
942  // insert a two byte length field here.
943  // This is a workaround to skip this length field:
944  uint16_t len = ((uint16_t)(*d) << 8) | *(d + 1);
945  if (len == l - 2) {
946  d += 2;
947  l -= 2;
948  }
949  }
950  unsigned char caepl = *d;
951  dbgprotocol(" %02X", caepl);
952  d += 1;
953  l -= 1;
954  bool ok = true;
955  if (l <= 2)
956  ok = CA_ENABLE(caepl) == CAEI_POSSIBLE;
957  while (l > 2) {
958  uint16_t pid = ((uint16_t)(*d) << 8) | *(d + 1);
959  unsigned char caees = *(d + 2);
960  dbgprotocol(" %d=%02X", pid, caees);
961  d += 3;
962  l -= 3;
963  if (CA_ENABLE(caees) != CAEI_POSSIBLE)
964  ok = false;
965  }
966  if (ok)
967  state = 6; // descrambling possible
968  }
969  }
970  }
971  dbgprotocol("\n");
972  }
973  break;
974  default: esyslog("ERROR: CAM %d: conditional access support: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
975  }
976  }
977  else if (state == 0) {
978  dbgprotocol("Slot %d: ==> Ca Info Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
980  state = 1; // enquired ca info
981  }
982  else if (state == 2 && timer.TimedOut()) {
983  cCiCaPmt CaPmt(CPCI_QUERY, 0, 0, 0, NULL);
984  SendPMT(&CaPmt);
986  state = 3; // waiting for reply
987  }
988  else if (state == 3 && timer.TimedOut()) {
989  dsyslog("CAM %d: doesn't reply to QUERY - only a single channel can be decrypted", Tc()->CamSlot()->SlotNumber());
990  state = 4; // normal operation
991  }
992 }
993 
995 {
996  if (CaPmt && state >= 2) {
997  dbgprotocol("Slot %d: ==> Ca Pmt (%d) %d %d\n", Tc()->CamSlot()->SlotNumber(), SessionId(), CaPmt->ListManagement(), CaPmt->CmdId());
998  SendData(AOT_CA_PMT, CaPmt->length, CaPmt->capmt);
999  state = 4; // sent ca pmt
1000  }
1001 }
1002 
1003 // --- cCiDateTime -----------------------------------------------------------
1004 
1005 class cCiDateTime : public cCiSession {
1006 private:
1008  time_t lastTime;
1009  void SendDateTime(void);
1010 public:
1012  virtual void Process(int Length = 0, const uint8_t *Data = NULL);
1013  };
1014 
1016 :cCiSession(SessionId, RI_DATE_TIME, Tc)
1017 {
1018  interval = 0;
1019  lastTime = 0;
1020  dbgprotocol("Slot %d: new Date Time (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
1021 }
1022 
1024 {
1025  time_t t = time(NULL);
1026  struct tm tm_gmt;
1027  struct tm tm_loc;
1028  if (gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc)) {
1029  int Y = tm_gmt.tm_year;
1030  int M = tm_gmt.tm_mon + 1;
1031  int D = tm_gmt.tm_mday;
1032  int L = (M == 1 || M == 2) ? 1 : 0;
1033  int MJD = 14956 + D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001);
1034 #define DEC2BCD(d) uint8_t(((d / 10) << 4) + (d % 10))
1035 #pragma pack(1)
1036  struct tTime { uint16_t mjd; uint8_t h, m, s; short offset; };
1037 #pragma pack()
1038  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)) };
1039  bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer;
1041  if (DumpDateTime)
1042  dbgprotocol("Slot %d: ==> Date Time (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
1043  SendData(AOT_DATE_TIME, 7, (uint8_t*)&T);
1044  DumpTPDUDataTransfer = OldDumpTPDUDataTransfer;
1045  }
1046 }
1047 
1048 void cCiDateTime::Process(int Length, const uint8_t *Data)
1049 {
1050  if (Data) {
1051  int Tag = GetTag(Length, &Data);
1052  switch (Tag) {
1053  case AOT_DATE_TIME_ENQ: {
1054  interval = 0;
1055  int l = 0;
1056  const uint8_t *d = GetData(Data, l);
1057  if (l > 0)
1058  interval = *d;
1059  dbgprotocol("Slot %d: <== Date Time Enq (%d), interval = %d\n", Tc()->CamSlot()->SlotNumber(), SessionId(), interval);
1060  lastTime = time(NULL);
1061  SendDateTime();
1062  }
1063  break;
1064  default: esyslog("ERROR: CAM %d: date time: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
1065  }
1066  }
1067  else if (interval && time(NULL) - lastTime > interval) {
1068  lastTime = time(NULL);
1069  SendDateTime();
1070  }
1071 }
1072 
1073 // --- cCiMMI ----------------------------------------------------------------
1074 
1075 // Display Control Commands:
1076 
1077 #define DCC_SET_MMI_MODE 0x01
1078 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
1079 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
1080 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
1081 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
1082 
1083 // MMI Modes:
1084 
1085 #define MM_HIGH_LEVEL 0x01
1086 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
1087 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
1088 
1089 // Display Reply IDs:
1090 
1091 #define DRI_MMI_MODE_ACK 0x01
1092 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
1093 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
1094 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
1095 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
1096 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
1097 #define DRI_UNKNOWN_MMI_MODE 0xF1
1098 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
1099 
1100 // Enquiry Flags:
1101 
1102 #define EF_BLIND 0x01
1103 
1104 // Answer IDs:
1105 
1106 #define AI_CANCEL 0x00
1107 #define AI_ANSWER 0x01
1108 
1109 class cCiMMI : public cCiSession {
1110 private:
1111  char *GetText(int &Length, const uint8_t **Data);
1112  cCiMenu *menu, *fetchedMenu;
1114 public:
1116  virtual ~cCiMMI();
1117  virtual void Process(int Length = 0, const uint8_t *Data = NULL);
1118  virtual bool HasUserIO(void) { return menu || enquiry; }
1119  cCiMenu *Menu(bool Clear = false);
1120  cCiEnquiry *Enquiry(bool Clear = false);
1121  void SendMenuAnswer(uint8_t Selection);
1122  bool SendAnswer(const char *Text);
1123  bool SendCloseMMI(void);
1124  };
1125 
1127 :cCiSession(SessionId, RI_MMI, Tc)
1128 {
1129  dbgprotocol("Slot %d: new MMI (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId);
1130  menu = fetchedMenu = NULL;
1131  enquiry = fetchedEnquiry = NULL;
1132 }
1133 
1135 {
1136  if (fetchedMenu) {
1137  cMutexLock MutexLock(fetchedMenu->mutex);
1138  fetchedMenu->mmi = NULL;
1139  }
1140  delete menu;
1141  if (fetchedEnquiry) {
1142  cMutexLock MutexLock(fetchedEnquiry->mutex);
1143  fetchedEnquiry->mmi = NULL;
1144  }
1145  delete enquiry;
1146 }
1147 
1148 char *cCiMMI::GetText(int &Length, const uint8_t **Data)
1152 {
1153  int Tag = GetTag(Length, Data);
1154  if (Tag == AOT_TEXT_LAST) {
1155  char *s = GetString(Length, Data);
1156  dbgprotocol("Slot %d: <== Text Last (%d) '%s'\n", Tc()->CamSlot()->SlotNumber(), SessionId(), s);
1157  return s;
1158  }
1159  else
1160  esyslog("ERROR: CAM %d: MMI: unexpected text tag: %06X", Tc()->CamSlot()->SlotNumber(), Tag);
1161  return NULL;
1162 }
1163 
1164 void cCiMMI::Process(int Length, const uint8_t *Data)
1165 {
1166  if (Data) {
1167  int Tag = GetTag(Length, &Data);
1168  switch (Tag) {
1169  case AOT_DISPLAY_CONTROL: {
1170  dbgprotocol("Slot %d: <== Display Control (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
1171  int l = 0;
1172  const uint8_t *d = GetData(Data, l);
1173  if (l > 0) {
1174  switch (*d) {
1175  case DCC_SET_MMI_MODE:
1176  if (l == 2 && *++d == MM_HIGH_LEVEL) {
1177  struct tDisplayReply { uint8_t id; uint8_t mode; };
1178  tDisplayReply dr = { id : DRI_MMI_MODE_ACK, mode : MM_HIGH_LEVEL };
1179  dbgprotocol("Slot %d: ==> Display Reply (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
1180  SendData(AOT_DISPLAY_REPLY, 2, (uint8_t *)&dr);
1181  }
1182  break;
1183  default: esyslog("ERROR: CAM %d: MMI: unsupported display control command %02X", Tc()->CamSlot()->SlotNumber(), *d);
1184  }
1185  }
1186  }
1187  break;
1188  case AOT_LIST_LAST:
1189  case AOT_MENU_LAST: {
1190  dbgprotocol("Slot %d: <== Menu Last (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
1191  delete menu;
1192  menu = new cCiMenu(this, Tag == AOT_MENU_LAST);
1193  int l = 0;
1194  const uint8_t *d = GetData(Data, l);
1195  if (l > 0) {
1196  // since the specification allows choiceNb to be undefined it is useless, so let's just skip it:
1197  d++;
1198  l--;
1199  if (l > 0) menu->titleText = GetText(l, &d);
1200  if (l > 0) menu->subTitleText = GetText(l, &d);
1201  if (l > 0) menu->bottomText = GetText(l, &d);
1202  while (l > 0) {
1203  char *s = GetText(l, &d);
1204  if (s) {
1205  if (!menu->AddEntry(s))
1206  free(s);
1207  }
1208  else
1209  break;
1210  }
1211  }
1212  }
1213  break;
1214  case AOT_ENQ: {
1215  dbgprotocol("Slot %d: <== Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
1216  delete enquiry;
1217  enquiry = new cCiEnquiry(this);
1218  int l = 0;
1219  const uint8_t *d = GetData(Data, l);
1220  if (l > 0) {
1221  uint8_t blind = *d++;
1222  //XXX GetByte()???
1223  l--;
1224  enquiry->blind = blind & EF_BLIND;
1225  enquiry->expectedLength = *d++;
1226  l--;
1227  // I really wonder why there is no text length field here...
1228  enquiry->text = CopyString(l, d);
1229  }
1230  }
1231  break;
1232  case AOT_CLOSE_MMI: {
1233  int id = -1;
1234  int delay = -1;
1235  int l = 0;
1236  const uint8_t *d = GetData(Data, l);
1237  if (l > 0) {
1238  id = *d++;
1239  if (l > 1)
1240  delay = *d;
1241  }
1242  dbgprotocol("Slot %d: <== Close MMI (%d) id = %02X delay = %d\n", Tc()->CamSlot()->SlotNumber(), SessionId(), id, delay);
1243  }
1244  break;
1245  default: esyslog("ERROR: CAM %d: MMI: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag);
1246  }
1247  }
1248 }
1249 
1250 cCiMenu *cCiMMI::Menu(bool Clear)
1251 {
1252  if (Clear)
1253  fetchedMenu = NULL;
1254  else if (menu) {
1255  fetchedMenu = menu;
1256  menu = NULL;
1257  }
1258  return fetchedMenu;
1259 }
1260 
1262 {
1263  if (Clear)
1264  fetchedEnquiry = NULL;
1265  else if (enquiry) {
1267  enquiry = NULL;
1268  }
1269  return fetchedEnquiry;
1270 }
1271 
1272 void cCiMMI::SendMenuAnswer(uint8_t Selection)
1273 {
1274  dbgprotocol("Slot %d: ==> Menu Answ (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
1275  SendData(AOT_MENU_ANSW, 1, &Selection);
1276 }
1277 
1278 bool cCiMMI::SendAnswer(const char *Text)
1279 {
1280  dbgprotocol("Slot %d: ==> Answ (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
1281  struct tAnswer { uint8_t id; char text[256]; };//XXX
1282  tAnswer answer;
1283  answer.id = Text ? AI_ANSWER : AI_CANCEL;
1284  if (Text)
1285  strncpy(answer.text, Text, sizeof(answer.text));
1286  SendData(AOT_ANSW, Text ? strlen(Text) + 1 : 1, (uint8_t *)&answer);
1287  return true;
1288 }
1289 
1291 {
1292  dbgprotocol("Slot %d: ==> Close MMI (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId());
1293  SendData(AOT_CLOSE_MMI, 0);
1294  return true;
1295 }
1296 
1297 // --- cCiMenu ---------------------------------------------------------------
1298 
1299 cCiMenu::cCiMenu(cCiMMI *MMI, bool Selectable)
1300 {
1301  mmi = MMI;
1302  mutex = NULL;
1303  selectable = Selectable;
1304  titleText = subTitleText = bottomText = NULL;
1305  numEntries = 0;
1306 }
1307 
1309 {
1310  cMutexLock MutexLock(mutex);
1311  if (mmi)
1312  mmi->Menu(true);
1313  free(titleText);
1314  free(subTitleText);
1315  free(bottomText);
1316  for (int i = 0; i < numEntries; i++)
1317  free(entries[i]);
1318 }
1319 
1320 bool cCiMenu::AddEntry(char *s)
1321 {
1322  if (numEntries < MAX_CIMENU_ENTRIES) {
1323  entries[numEntries++] = s;
1324  return true;
1325  }
1326  return false;
1327 }
1328 
1330 {
1331  // If the mmi is gone, the menu shall be closed, which also qualifies as 'update'.
1332  return !mmi || mmi->HasUserIO();
1333 }
1334 
1335 void cCiMenu::Select(int Index)
1336 {
1337  cMutexLock MutexLock(mutex);
1338  if (mmi && -1 <= Index && Index < numEntries)
1339  mmi->SendMenuAnswer(Index + 1);
1340 }
1341 
1343 {
1344  Select(-1);
1345 }
1346 
1347 void cCiMenu::Abort(void)
1348 {
1349  cMutexLock MutexLock(mutex);
1350  if (mmi)
1351  mmi->SendCloseMMI();
1352 }
1353 
1354 // --- cCiEnquiry ------------------------------------------------------------
1355 
1357 {
1358  mmi = MMI;
1359  text = NULL;
1360  blind = false;
1361  expectedLength = 0;
1362 }
1363 
1365 {
1366  cMutexLock MutexLock(mutex);
1367  if (mmi)
1368  mmi->Enquiry(true);
1369  free(text);
1370 }
1371 
1372 void cCiEnquiry::Reply(const char *s)
1373 {
1374  cMutexLock MutexLock(mutex);
1375  if (mmi)
1376  mmi->SendAnswer(s);
1377 }
1378 
1380 {
1381  Reply(NULL);
1382 }
1383 
1385 {
1386  cMutexLock MutexLock(mutex);
1387  if (mmi)
1388  mmi->SendCloseMMI();
1389 }
1390 
1391 // --- cCiTransportConnection (cont'd) ---------------------------------------
1392 
1393 #define TC_POLL_TIMEOUT 300 // ms WORKAROUND: TC_POLL_TIMEOUT < 300ms doesn't work with DragonCAM
1394 #define TC_ALIVE_TIMEOUT 2000 // ms after which a transport connection is assumed dead
1395 
1397 {
1398  dbgprotocol("Slot %d: creating connection %d/%d\n", CamSlot->SlotNumber(), CamSlot->SlotIndex(), Tcid);
1399  camSlot = CamSlot;
1400  tcid = Tcid;
1401  state = stIDLE;
1402  createConnectionRequested = false;
1403  deleteConnectionRequested = false;
1404  hasUserIO = false;
1405  alive.Set(TC_ALIVE_TIMEOUT);
1406  for (int i = 0; i <= MAX_SESSIONS_PER_TC; i++) // sessions[0] is not used, but initialized anyway
1407  sessions[i] = NULL;
1408 }
1409 
1411 {
1412  for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++)
1413  delete sessions[i];
1414 }
1415 
1417 {
1419  return cas && cas->Ready();
1420 }
1421 
1423 {
1425  return ai ? ai->GetMenuString() : NULL;
1426 }
1427 
1428 void cCiTransportConnection::SendTPDU(uint8_t Tag, int Length, const uint8_t *Data)
1429 {
1430  cTPDU TPDU(camSlot->SlotIndex(), tcid, Tag, Length, Data);
1431  camSlot->Write(&TPDU);
1432  timer.Set(TC_POLL_TIMEOUT);
1433 }
1434 
1435 void cCiTransportConnection::SendData(int Length, const uint8_t *Data)
1436 {
1437  // if Length ever exceeds MAX_TPDU_DATA this needs to be handled differently
1438  if (state == stACTIVE && Length > 0)
1439  SendTPDU(T_DATA_LAST, Length, Data);
1440 }
1441 
1442 void cCiTransportConnection::SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId, int Status)
1443 {
1444  uint8_t buffer[16];
1445  uint8_t *p = buffer;
1446  *p++ = Tag;
1447  *p++ = 0x00; // will contain length
1448  if (Status >= 0)
1449  *p++ = Status;
1450  if (ResourceId) {
1451  put_unaligned(htonl(ResourceId), (uint32_t *)p);
1452  p += 4;
1453  }
1454  put_unaligned(htons(SessionId), (uint16_t *)p);
1455  p += 2;
1456  buffer[1] = p - buffer - 2; // length
1457  SendData(p - buffer, buffer);
1458 }
1459 
1461 {
1462  bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer;
1464  if (DumpPolls)
1465  dbgprotocol("Slot %d: ==> Poll\n", camSlot->SlotNumber());
1466  SendTPDU(T_DATA_LAST);
1467  DumpTPDUDataTransfer = OldDumpTPDUDataTransfer;
1468 }
1469 
1470 uint32_t cCiTransportConnection::ResourceIdToInt(const uint8_t *Data)
1471 {
1472  return (ntohl(get_unaligned((uint32_t *)Data)));
1473 }
1474 
1476 {
1477  return (SessionId <= MAX_SESSIONS_PER_TC) ? sessions[SessionId] : NULL;
1478 }
1479 
1481 {
1482  for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
1483  if (sessions[i] && sessions[i]->ResourceId() == ResourceId)
1484  return sessions[i];
1485  }
1486  return NULL;
1487 }
1488 
1489 void cCiTransportConnection::OpenSession(int Length, const uint8_t *Data)
1490 {
1491  if (Length == 6 && *(Data + 1) == 0x04) {
1492  uint32_t ResourceId = ResourceIdToInt(Data + 2);
1493  dbgprotocol("Slot %d: open session %08X\n", camSlot->SlotNumber(), ResourceId);
1494  if (!GetSessionByResourceId(ResourceId)) {
1495  for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
1496  if (!sessions[i]) {
1497  switch (ResourceId) {
1498  case RI_RESOURCE_MANAGER: sessions[i] = new cCiResourceManager(i, this); break;
1499  case RI_APPLICATION_INFORMATION: sessions[i] = new cCiApplicationInformation(i, this); break;
1500  case RI_CONDITIONAL_ACCESS_SUPPORT: sessions[i] = new cCiConditionalAccessSupport(i, this); break;
1501  case RI_DATE_TIME: sessions[i] = new cCiDateTime(i, this); break;
1502  case RI_MMI: sessions[i] = new cCiMMI(i, this); break;
1503  case RI_HOST_CONTROL: // not implemented
1504  default: esyslog("ERROR: CAM %d: unknown resource identifier: %08X (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
1505  }
1506  if (sessions[i])
1507  SendTag(ST_OPEN_SESSION_RESPONSE, sessions[i]->SessionId(), sessions[i]->ResourceId(), SS_OK);
1508  return;
1509  }
1510  }
1511  esyslog("ERROR: CAM %d: no free session slot for resource identifier %08X (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
1512  }
1513  else
1514  esyslog("ERROR: CAM %d: session for resource identifier %08X already exists (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
1515  }
1516 }
1517 
1519 {
1520  dbgprotocol("Slot %d: close session %d\n", camSlot->SlotNumber(), SessionId);
1521  cCiSession *Session = GetSessionBySessionId(SessionId);
1522  if (Session && sessions[SessionId] == Session) {
1523  delete Session;
1524  sessions[SessionId] = NULL;
1525  SendTag(ST_CLOSE_SESSION_RESPONSE, SessionId, 0, SS_OK);
1526  }
1527  else {
1528  esyslog("ERROR: CAM %d: unknown session id: %d (%d/%d)", camSlot->SlotNumber(), SessionId, camSlot->SlotIndex(), tcid);
1529  SendTag(ST_CLOSE_SESSION_RESPONSE, SessionId, 0, SS_NOT_ALLOCATED);
1530  }
1531 }
1532 
1534 {
1535  int Length;
1536  const uint8_t *Data = TPDU->Data(Length);
1537  if (Data && Length > 1) {
1538  switch (*Data) {
1539  case ST_SESSION_NUMBER: if (Length > 4) {
1540  uint16_t SessionId = ntohs(get_unaligned((uint16_t *)&Data[2]));
1541  cCiSession *Session = GetSessionBySessionId(SessionId);
1542  if (Session)
1543  Session->Process(Length - 4, Data + 4);
1544  else
1545  esyslog("ERROR: CAM %d: unknown session id: %d (%d/%d)", camSlot->SlotNumber(), SessionId, camSlot->SlotIndex(), tcid);
1546  }
1547  break;
1548  case ST_OPEN_SESSION_REQUEST: OpenSession(Length, Data);
1549  break;
1550  case ST_CLOSE_SESSION_REQUEST: if (Length == 4)
1551  CloseSession(ntohs(get_unaligned((uint16_t *)&Data[2])));
1552  break;
1553  case ST_CREATE_SESSION_RESPONSE: // not implemented
1554  case ST_CLOSE_SESSION_RESPONSE: // not implemented
1555  default: esyslog("ERROR: CAM %d: unknown session tag: %02X (%d/%d)", camSlot->SlotNumber(), *Data, camSlot->SlotIndex(), tcid);
1556  }
1557  }
1558 }
1559 
1561 {
1562  if (TPDU)
1563  alive.Set(TC_ALIVE_TIMEOUT);
1564  else if (alive.TimedOut())
1565  return false;
1566  switch (state) {
1567  case stIDLE:
1568  if (createConnectionRequested) {
1569  dbgprotocol("Slot %d: create connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1570  createConnectionRequested = false;
1571  SendTPDU(T_CREATE_TC);
1572  state = stCREATION;
1573  }
1574  return true;
1575  case stCREATION:
1576  if (TPDU && TPDU->Tag() == T_CTC_REPLY) {
1577  dbgprotocol("Slot %d: connection created %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1578  Poll();
1579  state = stACTIVE;
1580  }
1581  else if (timer.TimedOut()) {
1582  dbgprotocol("Slot %d: timeout while creating connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1583  state = stIDLE;
1584  }
1585  return true;
1586  case stACTIVE:
1587  if (deleteConnectionRequested) {
1588  dbgprotocol("Slot %d: delete connection requested %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1589  deleteConnectionRequested = false;
1590  SendTPDU(T_DELETE_TC);
1591  state = stDELETION;
1592  return true;
1593  }
1594  if (TPDU) {
1595  switch (TPDU->Tag()) {
1596  case T_REQUEST_TC:
1597  esyslog("ERROR: CAM %d: T_REQUEST_TC not implemented (%d/%d)", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1598  break;
1599  case T_DATA_MORE:
1600  case T_DATA_LAST:
1601  HandleSessions(TPDU);
1602  // continue with T_SB
1603  case T_SB:
1604  if ((TPDU->Status() & DATA_INDICATOR) != 0) {
1605  dbgprotocol("Slot %d: receive data %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1606  SendTPDU(T_RCV);
1607  }
1608  break;
1609  case T_DELETE_TC:
1610  dbgprotocol("Slot %d: delete connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1611  SendTPDU(T_DTC_REPLY);
1612  state = stIDLE;
1613  return true;
1614  case T_RCV:
1615  case T_CREATE_TC:
1616  case T_CTC_REPLY:
1617  case T_DTC_REPLY:
1618  case T_NEW_TC:
1619  case T_TC_ERROR:
1620  break;
1621  default:
1622  esyslog("ERROR: unknown TPDU tag: 0x%02X (%s)", TPDU->Tag(), __FUNCTION__);
1623  }
1624  }
1625  else if (timer.TimedOut())
1626  Poll();
1627  hasUserIO = false;
1628  for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
1629  if (sessions[i]) {
1630  sessions[i]->Process();
1631  if (sessions[i]->HasUserIO())
1632  hasUserIO = true;
1633  }
1634  }
1635  break;
1636  case stDELETION:
1637  if (TPDU && TPDU->Tag() == T_DTC_REPLY || timer.TimedOut()) {
1638  dbgprotocol("Slot %d: connection deleted %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1639  state = stIDLE;
1640  }
1641  return true;
1642  default:
1643  esyslog("ERROR: unknown state: %d (%s)", state, __FUNCTION__);
1644  }
1645  return true;
1646 }
1647 
1648 // --- cCiCaPidData ----------------------------------------------------------
1649 
1650 class cCiCaPidData : public cListObject {
1651 public:
1652  bool active;
1653  int pid;
1655  cCiCaPidData(int Pid, int StreamType)
1656  {
1657  active = false;
1658  pid = Pid;
1659  streamType = StreamType;
1660  }
1661  };
1662 
1663 // --- cCiCaProgramData ------------------------------------------------------
1664 
1666 public:
1668  bool modified;
1670  cCiCaProgramData(int ProgramNumber)
1671  {
1672  programNumber = ProgramNumber;
1673  modified = false;
1674  }
1675  };
1676 
1677 // --- cCiAdapter ------------------------------------------------------------
1678 
1680 :cThread("CI adapter")
1681 {
1682  for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++)
1683  camSlots[i] = NULL;
1684 }
1685 
1687 {
1688  Cancel(3);
1689  for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++)
1690  delete camSlots[i];
1691 }
1692 
1694 {
1695  if (CamSlot) {
1696  for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) {
1697  if (!camSlots[i]) {
1698  CamSlot->slotIndex = i;
1699  camSlots[i] = CamSlot;
1700  return;
1701  }
1702  }
1703  esyslog("ERROR: no free CAM slot in CI adapter");
1704  }
1705 }
1706 
1708 {
1709  if (Iter >= 0) {
1710  for (; Iter < MAX_CAM_SLOTS_PER_ADAPTER; ) {
1711  if (cCamSlot *Found = camSlots[Iter++])
1712  return Found;
1713  }
1714  }
1715  return NULL;
1716 }
1717 
1719 {
1720  cTPDU TPDU;
1721  while (Running()) {
1722  int n = Read(TPDU.Buffer(), TPDU.MaxSize());
1723  if (n > 0 && TPDU.Slot() < MAX_CAM_SLOTS_PER_ADAPTER) {
1724  TPDU.SetSize(n);
1725  cCamSlot *cs = camSlots[TPDU.Slot()];
1726  TPDU.Dump(cs ? cs->SlotNumber() : 0, false);
1727  if (cs)
1728  cs->Process(&TPDU);
1729  }
1730  for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) {
1731  if (camSlots[i])
1732  camSlots[i]->Process();
1733  }
1734  }
1735 }
1736 
1737 // --- cCamSlot --------------------------------------------------------------
1738 
1739 #define MODULE_CHECK_INTERVAL 500 // ms
1740 #define MODULE_RESET_TIMEOUT 2 // s
1741 
1742 cCamSlot::cCamSlot(cCiAdapter *CiAdapter, bool ReceiveCaPids)
1743 {
1744  ciAdapter = CiAdapter;
1745  assignedDevice = NULL;
1746  caPidReceiver = ReceiveCaPids ? new cCaPidReceiver : NULL;
1747  caActivationReceiver = NULL;
1748  slotIndex = -1;
1749  lastModuleStatus = msReset; // avoids initial reset log message
1750  resetTime = 0;
1751  resendPmt = false;
1752  source = transponder = 0;
1753  for (int i = 0; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) // tc[0] is not used, but initialized anyway
1754  tc[i] = NULL;
1755  CamSlots.Add(this);
1756  slotNumber = Index() + 1;
1757  if (ciAdapter)
1758  ciAdapter->AddCamSlot(this);
1759  Reset();
1760 }
1761 
1763 {
1764  if (assignedDevice)
1765  assignedDevice->SetCamSlot(NULL);
1766  delete caPidReceiver;
1767  delete caActivationReceiver;
1768  CamSlots.Del(this, false);
1769  DeleteAllConnections();
1770 }
1771 
1772 bool cCamSlot::Assign(cDevice *Device, bool Query)
1773 {
1774  cMutexLock MutexLock(&mutex);
1775  if (ciAdapter) {
1776  if (ciAdapter->Assign(Device, true)) {
1777  if (!Device && assignedDevice)
1778  assignedDevice->SetCamSlot(NULL);
1779  if (!Query || !Device) {
1780  StopDecrypting();
1781  source = transponder = 0;
1782  if (ciAdapter->Assign(Device)) {
1783  assignedDevice = Device;
1784  if (Device) {
1785  Device->SetCamSlot(this);
1786  dsyslog("CAM %d: assigned to device %d", slotNumber, Device->DeviceNumber() + 1);
1787  }
1788  else {
1789  CancelActivation();
1790  dsyslog("CAM %d: unassigned", slotNumber);
1791  }
1792  }
1793  else
1794  return false;
1795  }
1796  return true;
1797  }
1798  }
1799  return false;
1800 }
1801 
1803 {
1804  cMutexLock MutexLock(&mutex);
1805  for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) {
1806  if (!tc[i]) {
1807  tc[i] = new cCiTransportConnection(this, i);
1808  tc[i]->CreateConnection();
1809  return;
1810  }
1811  }
1812  esyslog("ERROR: CAM %d: can't create new transport connection!", slotNumber);
1813 }
1814 
1816 {
1817  cMutexLock MutexLock(&mutex);
1818  for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) {
1819  delete tc[i];
1820  tc[i] = NULL;
1821  }
1822 }
1823 
1825 {
1826  cMutexLock MutexLock(&mutex);
1827  if (caActivationReceiver && !caActivationReceiver->IsAttached())
1828  CancelActivation();
1829  if (TPDU) {
1830  int n = TPDU->Tcid();
1831  if (1 <= n && n <= MAX_CONNECTIONS_PER_CAM_SLOT) {
1832  if (tc[n])
1833  tc[n]->Process(TPDU);
1834  }
1835  }
1836  for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) {
1837  if (tc[i]) {
1838  if (!tc[i]->Process()) {
1839  Reset();
1840  return;
1841  }
1842  }
1843  }
1844  if (moduleCheckTimer.TimedOut()) {
1845  eModuleStatus ms = ModuleStatus();
1846  if (ms != lastModuleStatus) {
1847  switch (ms) {
1848  case msNone:
1849  dbgprotocol("Slot %d: no module present\n", slotNumber);
1850  isyslog("CAM %d: no module present", slotNumber);
1851  DeleteAllConnections();
1852  CancelActivation();
1853  break;
1854  case msReset:
1855  dbgprotocol("Slot %d: module reset\n", slotNumber);
1856  isyslog("CAM %d: module reset", slotNumber);
1857  DeleteAllConnections();
1858  break;
1859  case msPresent:
1860  dbgprotocol("Slot %d: module present\n", slotNumber);
1861  isyslog("CAM %d: module present", slotNumber);
1862  break;
1863  case msReady:
1864  dbgprotocol("Slot %d: module ready\n", slotNumber);
1865  isyslog("CAM %d: module ready", slotNumber);
1866  NewConnection();
1867  resendPmt = caProgramList.Count() > 0;
1868  break;
1869  default:
1870  esyslog("ERROR: unknown module status %d (%s)", ms, __FUNCTION__);
1871  }
1872  lastModuleStatus = ms;
1873  }
1874  moduleCheckTimer.Set(MODULE_CHECK_INTERVAL);
1875  }
1876  if (resendPmt)
1877  SendCaPmt(CPCI_OK_DESCRAMBLING);
1878  processed.Broadcast();
1879 }
1880 
1882 {
1883  cMutexLock MutexLock(&mutex);
1884  return tc[1] ? tc[1]->GetSessionByResourceId(ResourceId) : NULL;
1885 }
1886 
1888 {
1889  cMutexLock MutexLock(&mutex);
1890  if (ciAdapter && TPDU->Size()) {
1891  TPDU->Dump(SlotNumber(), true);
1892  ciAdapter->Write(TPDU->Buffer(), TPDU->Size());
1893  }
1894 }
1895 
1897 {
1898  cMutexLock MutexLock(&mutex);
1899  ChannelCamRelations.Reset(slotNumber);
1900  DeleteAllConnections();
1901  if (ciAdapter) {
1902  dbgprotocol("Slot %d: reset...", slotNumber);
1903  if (ciAdapter->Reset(slotIndex)) {
1904  resetTime = time(NULL);
1905  dbgprotocol("ok.\n");
1906  lastModuleStatus = msReset;
1907  return true;
1908  }
1909  dbgprotocol("failed!\n");
1910  }
1911  return false;
1912 }
1913 
1915 {
1916  return ModuleStatus() == msReady;
1917 }
1918 
1920 {
1921  cMutexLock MutexLock(&mutex);
1922  if (!caActivationReceiver) {
1923  if (cDevice *d = Device()) {
1925  caActivationReceiver = new cCaActivationReceiver(Channel, this);
1926  d->AttachReceiver(caActivationReceiver);
1927  dsyslog("CAM %d: activating on device %d with channel %d (%s)", SlotNumber(), d->DeviceNumber() + 1, Channel->Number(), Channel->Name());
1928  }
1929  }
1930  }
1931 }
1932 
1934 {
1935  cMutexLock MutexLock(&mutex);
1936  delete caActivationReceiver;
1937  caActivationReceiver = NULL;
1938 }
1939 
1941 {
1942  return caActivationReceiver;
1943 }
1944 
1946 {
1947  cMutexLock MutexLock(&mutex);
1948  eModuleStatus ms = ciAdapter ? ciAdapter->ModuleStatus(slotIndex) : msNone;
1949  if (resetTime) {
1950  if (ms <= msReset) {
1951  if (time(NULL) - resetTime < MODULE_RESET_TIMEOUT)
1952  return msReset;
1953  }
1954  resetTime = 0;
1955  }
1956  return ms;
1957 }
1958 
1959 const char *cCamSlot::GetCamName(void)
1960 {
1961  cMutexLock MutexLock(&mutex);
1962  return tc[1] ? tc[1]->GetCamName() : NULL;
1963 }
1964 
1966 {
1967  cMutexLock MutexLock(&mutex);
1968  return ModuleStatus() == msNone || tc[1] && tc[1]->Ready();
1969 }
1970 
1972 {
1973  return GetSessionByResourceId(RI_MMI);
1974 }
1975 
1977 {
1978  cMutexLock MutexLock(&mutex);
1979  return tc[1] && tc[1]->HasUserIO();
1980 }
1981 
1983 {
1984  cMutexLock MutexLock(&mutex);
1986  return api ? api->EnterMenu() : false;
1987 }
1988 
1990 {
1991  cMutexLock MutexLock(&mutex);
1992  cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI);
1993  if (mmi) {
1994  cCiMenu *Menu = mmi->Menu();
1995  if (Menu)
1996  Menu->mutex = &mutex;
1997  return Menu;
1998  }
1999  return NULL;
2000 }
2001 
2003 {
2004  cMutexLock MutexLock(&mutex);
2005  cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI);
2006  if (mmi) {
2007  cCiEnquiry *Enquiry = mmi->Enquiry();
2008  if (Enquiry)
2009  Enquiry->mutex = &mutex;
2010  return Enquiry;
2011  }
2012  return NULL;
2013 }
2014 
2015 void cCamSlot::SendCaPmt(uint8_t CmdId)
2016 {
2017  cMutexLock MutexLock(&mutex);
2019  if (cas) {
2020  const int *CaSystemIds = cas->GetCaSystemIds();
2021  if (CaSystemIds && *CaSystemIds) {
2022  if (caProgramList.Count()) {
2023  if (caPidReceiver && caPidReceiver->HasCaPids()) {
2024  if (cDevice *d = Device())
2025  d->Detach(caPidReceiver);
2026  }
2027  for (int Loop = 1; Loop <= 2; Loop++) {
2028  for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
2029  if (p->modified || resendPmt) {
2030  bool Active = false;
2031  cCiCaPmt CaPmt(CmdId, source, transponder, p->programNumber, CaSystemIds);
2032  for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
2033  if (q->active) {
2034  CaPmt.AddPid(q->pid, q->streamType);
2035  Active = true;
2036  }
2037  }
2038  if ((Loop == 1) != Active) { // first remove, then add
2039  if (caPidReceiver) {
2040  int CaPids[MAXRECEIVEPIDS + 1];
2041  if (GetCaPids(source, transponder, p->programNumber, CaSystemIds, MAXRECEIVEPIDS + 1, CaPids) > 0) {
2042  if (Loop == 1)
2043  caPidReceiver->DelPids(CaPids);
2044  else
2045  caPidReceiver->AddPids(CaPids);
2046  }
2047  }
2048  if (cas->RepliesToQuery())
2049  CaPmt.SetListManagement(Active ? CPLM_ADD : CPLM_UPDATE);
2050  if (Active || cas->RepliesToQuery())
2051  cas->SendPMT(&CaPmt);
2052  p->modified = false;
2053  }
2054  }
2055  }
2056  }
2057  if (caPidReceiver && caPidReceiver->HasCaPids()) {
2058  if (cDevice *d = Device())
2059  d->AttachReceiver(caPidReceiver);
2060  }
2061  resendPmt = false;
2062  }
2063  else {
2064  cCiCaPmt CaPmt(CmdId, 0, 0, 0, NULL);
2065  cas->SendPMT(&CaPmt);
2066  if (caPidReceiver) {
2067  if (cDevice *d = Device())
2068  d->Detach(caPidReceiver);
2069  caPidReceiver->Reset();
2070  }
2071  }
2072  }
2073  }
2074 }
2075 
2077 {
2078  cMutexLock MutexLock(&mutex);
2080  return cas ? cas->GetCaSystemIds() : NULL;
2081 }
2082 
2084 {
2085  cDevice *d = Device();
2086  return d ? d->Priority() : IDLEPRIORITY;
2087 }
2088 
2089 bool cCamSlot::ProvidesCa(const int *CaSystemIds)
2090 {
2091  cMutexLock MutexLock(&mutex);
2093  if (cas) {
2094  for (const int *ids = cas->GetCaSystemIds(); ids && *ids; ids++) {
2095  for (const int *id = CaSystemIds; *id; id++) {
2096  if (*id == *ids)
2097  return true;
2098  }
2099  }
2100  }
2101  return false;
2102 }
2103 
2104 void cCamSlot::AddPid(int ProgramNumber, int Pid, int StreamType)
2105 {
2106  cMutexLock MutexLock(&mutex);
2107  cCiCaProgramData *ProgramData = NULL;
2108  for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
2109  if (p->programNumber == ProgramNumber) {
2110  ProgramData = p;
2111  for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
2112  if (q->pid == Pid)
2113  return;
2114  }
2115  }
2116  }
2117  if (!ProgramData)
2118  caProgramList.Add(ProgramData = new cCiCaProgramData(ProgramNumber));
2119  ProgramData->pidList.Add(new cCiCaPidData(Pid, StreamType));
2120 }
2121 
2122 void cCamSlot::SetPid(int Pid, bool Active)
2123 {
2124  cMutexLock MutexLock(&mutex);
2125  for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
2126  for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
2127  if (q->pid == Pid) {
2128  if (q->active != Active) {
2129  q->active = Active;
2130  p->modified = true;
2131  }
2132  return;
2133  }
2134  }
2135  }
2136 }
2137 
2138 // see ISO/IEC 13818-1
2139 #define STREAM_TYPE_VIDEO 0x02
2140 #define STREAM_TYPE_AUDIO 0x04
2141 #define STREAM_TYPE_PRIVATE 0x06
2142 
2143 void cCamSlot::AddChannel(const cChannel *Channel)
2144 {
2145  cMutexLock MutexLock(&mutex);
2146  if (source != Channel->Source() || transponder != Channel->Transponder())
2147  StopDecrypting();
2148  source = Channel->Source();
2149  transponder = Channel->Transponder();
2150  if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
2151  AddPid(Channel->Sid(), Channel->Vpid(), STREAM_TYPE_VIDEO);
2152  for (const int *Apid = Channel->Apids(); *Apid; Apid++)
2153  AddPid(Channel->Sid(), *Apid, STREAM_TYPE_AUDIO);
2154  for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
2155  AddPid(Channel->Sid(), *Dpid, STREAM_TYPE_PRIVATE);
2156  for (const int *Spid = Channel->Spids(); *Spid; Spid++)
2157  AddPid(Channel->Sid(), *Spid, STREAM_TYPE_PRIVATE);
2158  if (Channel->Tpid() && Setup.SupportTeletext)
2159  AddPid(Channel->Sid(), Channel->Tpid(), STREAM_TYPE_PRIVATE);
2160  }
2161 }
2162 
2163 #define QUERY_REPLY_WAIT 100 // ms to wait between checks for a reply
2164 
2165 bool cCamSlot::CanDecrypt(const cChannel *Channel)
2166 {
2167  if (Channel->Ca() < CA_ENCRYPTED_MIN)
2168  return true; // channel not encrypted
2169  if (!IsDecrypting())
2170  return true; // any CAM can decrypt at least one channel
2171  cMutexLock MutexLock(&mutex);
2173  if (cas && cas->RepliesToQuery()) {
2174  cCiCaPmt CaPmt(CPCI_QUERY, Channel->Source(), Channel->Transponder(), Channel->Sid(), GetCaSystemIds());
2175  CaPmt.SetListManagement(CPLM_ADD); // WORKAROUND: CPLM_ONLY doesn't work with Alphacrypt 3.09 (deletes existing CA_PMTs)
2176  CaPmt.AddPid(Channel->Vpid(), STREAM_TYPE_VIDEO);
2177  for (const int *Apid = Channel->Apids(); *Apid; Apid++)
2178  CaPmt.AddPid(*Apid, STREAM_TYPE_AUDIO);
2179  for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
2180  CaPmt.AddPid(*Dpid, STREAM_TYPE_PRIVATE);
2181  for (const int *Spid = Channel->Spids(); *Spid; Spid++)
2182  CaPmt.AddPid(*Spid, STREAM_TYPE_PRIVATE);
2183  if (Channel->Tpid() && Setup.SupportTeletext) {
2184  CaPmt.AddPid(Channel->Tpid(), STREAM_TYPE_PRIVATE);
2185  }
2186  cas->SendPMT(&CaPmt);
2187  cTimeMs Timeout(QUERY_REPLY_TIMEOUT);
2188  do {
2189  processed.TimedWait(mutex, QUERY_REPLY_WAIT);
2190  if ((cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT)) != NULL) { // must re-fetch it, there might have been a reset
2191  if (cas->ReceivedReply())
2192  return cas->CanDecrypt();
2193  }
2194  else
2195  return false;
2196  } while (!Timeout.TimedOut());
2197  dsyslog("CAM %d: didn't reply to QUERY", SlotNumber());
2198  }
2199  return false;
2200 }
2201 
2203 {
2204  SendCaPmt(CPCI_OK_DESCRAMBLING);
2205 }
2206 
2208 {
2209  cMutexLock MutexLock(&mutex);
2210  if (caProgramList.Count()) {
2211  caProgramList.Clear();
2212  SendCaPmt(CPCI_NOT_SELECTED);
2213  }
2214 }
2215 
2217 {
2218  cMutexLock MutexLock(&mutex);
2219  if (caProgramList.Count()) {
2220  for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
2221  if (p->modified)
2222  return true; // any modifications need to be processed before we can assume it's no longer decrypting
2223  for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
2224  if (q->active)
2225  return true;
2226  }
2227  }
2228  }
2229  return false;
2230 }
2231 
2232 uchar *cCamSlot::Decrypt(uchar *Data, int &Count)
2233 {
2234  Count = TS_SIZE;
2235  return Data;
2236 }
2237 
2238 // --- cCamSlots -------------------------------------------------------------
2239 
2241 
2243 {
2244  for (time_t t0 = time(NULL); time(NULL) - t0 < Timeout; ) {
2245  bool ready = true;
2246  for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
2247  if (!CamSlot->Ready()) {
2248  ready = false;
2249  cCondWait::SleepMs(100);
2250  }
2251  }
2252  if (ready)
2253  return true;
2254  }
2255  return false;
2256 }
2257 
2258 // --- cChannelCamRelation ---------------------------------------------------
2259 
2260 #define CAM_CHECKED_TIMEOUT 15 // seconds before a CAM that has been checked for a particular channel will be checked again
2261 
2263 private:
2267  time_t lastChecked;
2268 public:
2269  cChannelCamRelation(tChannelID ChannelID);
2270  bool TimedOut(void);
2271  tChannelID ChannelID(void) { return channelID; }
2272  bool CamChecked(int CamSlotNumber);
2273  bool CamDecrypt(int CamSlotNumber);
2274  void SetChecked(int CamSlotNumber);
2275  void SetDecrypt(int CamSlotNumber);
2276  void ClrChecked(int CamSlotNumber);
2277  void ClrDecrypt(int CamSlotNumber);
2278  };
2279 
2281 {
2282  channelID = ChannelID;
2283  camSlotsChecked = 0;
2284  camSlotsDecrypt = 0;
2285  lastChecked = 0;
2286 }
2287 
2289 {
2290  return !camSlotsDecrypt && time(NULL) - lastChecked > CAM_CHECKED_TIMEOUT;
2291 }
2292 
2293 bool cChannelCamRelation::CamChecked(int CamSlotNumber)
2294 {
2295  if (lastChecked && time(NULL) - lastChecked > CAM_CHECKED_TIMEOUT) {
2296  lastChecked = 0;
2297  camSlotsChecked = 0;
2298  }
2299  return camSlotsChecked & (1 << (CamSlotNumber - 1));
2300 }
2301 
2302 bool cChannelCamRelation::CamDecrypt(int CamSlotNumber)
2303 {
2304  return camSlotsDecrypt & (1 << (CamSlotNumber - 1));
2305 }
2306 
2307 void cChannelCamRelation::SetChecked(int CamSlotNumber)
2308 {
2309  camSlotsChecked |= (1 << (CamSlotNumber - 1));
2310  lastChecked = time(NULL);
2311  ClrDecrypt(CamSlotNumber);
2312 }
2313 
2314 void cChannelCamRelation::SetDecrypt(int CamSlotNumber)
2315 {
2316  camSlotsDecrypt |= (1 << (CamSlotNumber - 1));
2317  ClrChecked(CamSlotNumber);
2318 }
2319 
2320 void cChannelCamRelation::ClrChecked(int CamSlotNumber)
2321 {
2322  camSlotsChecked &= ~(1 << (CamSlotNumber - 1));
2323  lastChecked = 0;
2324 }
2325 
2326 void cChannelCamRelation::ClrDecrypt(int CamSlotNumber)
2327 {
2328  camSlotsDecrypt &= ~(1 << (CamSlotNumber - 1));
2329 }
2330 
2331 // --- cChannelCamRelations --------------------------------------------------
2332 
2333 #define CHANNEL_CAM_RELATIONS_CLEANUP_INTERVAL 3600 // seconds between cleanups
2334 
2336 
2338 {
2339  lastCleanup = time(NULL);
2340 }
2341 
2343 {
2344  cMutexLock MutexLock(&mutex);
2345  if (time(NULL) - lastCleanup > CHANNEL_CAM_RELATIONS_CLEANUP_INTERVAL) {
2346  for (cChannelCamRelation *ccr = First(); ccr; ) {
2347  cChannelCamRelation *c = ccr;
2348  ccr = Next(ccr);
2349  if (c->TimedOut())
2350  Del(c);
2351  }
2352  lastCleanup = time(NULL);
2353  }
2354 }
2355 
2357 {
2358  cMutexLock MutexLock(&mutex);
2359  Cleanup();
2360  for (cChannelCamRelation *ccr = First(); ccr; ccr = Next(ccr)) {
2361  if (ccr->ChannelID() == ChannelID)
2362  return ccr;
2363  }
2364  return NULL;
2365 }
2366 
2368 {
2369  cMutexLock MutexLock(&mutex);
2370  cChannelCamRelation *ccr = GetEntry(ChannelID);
2371  if (!ccr)
2372  Add(ccr = new cChannelCamRelation(ChannelID));
2373  return ccr;
2374 }
2375 
2376 void cChannelCamRelations::Reset(int CamSlotNumber)
2377 {
2378  cMutexLock MutexLock(&mutex);
2379  for (cChannelCamRelation *ccr = First(); ccr; ccr = Next(ccr)) {
2380  ccr->ClrChecked(CamSlotNumber);
2381  ccr->ClrDecrypt(CamSlotNumber);
2382  }
2383 }
2384 
2385 bool cChannelCamRelations::CamChecked(tChannelID ChannelID, int CamSlotNumber)
2386 {
2387  cMutexLock MutexLock(&mutex);
2388  cChannelCamRelation *ccr = GetEntry(ChannelID);
2389  return ccr ? ccr->CamChecked(CamSlotNumber) : false;
2390 }
2391 
2392 bool cChannelCamRelations::CamDecrypt(tChannelID ChannelID, int CamSlotNumber)
2393 {
2394  cMutexLock MutexLock(&mutex);
2395  cChannelCamRelation *ccr = GetEntry(ChannelID);
2396  return ccr ? ccr->CamDecrypt(CamSlotNumber) : false;
2397 }
2398 
2399 void cChannelCamRelations::SetChecked(tChannelID ChannelID, int CamSlotNumber)
2400 {
2401  cMutexLock MutexLock(&mutex);
2402  cChannelCamRelation *ccr = AddEntry(ChannelID);
2403  if (ccr)
2404  ccr->SetChecked(CamSlotNumber);
2405 }
2406 
2407 void cChannelCamRelations::SetDecrypt(tChannelID ChannelID, int CamSlotNumber)
2408 {
2409  cMutexLock MutexLock(&mutex);
2410  cChannelCamRelation *ccr = AddEntry(ChannelID);
2411  if (ccr)
2412  ccr->SetDecrypt(CamSlotNumber);
2413 }
2414 
2415 void cChannelCamRelations::ClrChecked(tChannelID ChannelID, int CamSlotNumber)
2416 {
2417  cMutexLock MutexLock(&mutex);
2418  cChannelCamRelation *ccr = GetEntry(ChannelID);
2419  if (ccr)
2420  ccr->ClrChecked(CamSlotNumber);
2421 }
2422 
2423 void cChannelCamRelations::ClrDecrypt(tChannelID ChannelID, int CamSlotNumber)
2424 {
2425  cMutexLock MutexLock(&mutex);
2426  cChannelCamRelation *ccr = GetEntry(ChannelID);
2427  if (ccr)
2428  ccr->ClrDecrypt(CamSlotNumber);
2429 }
uint16_t applicationManufacturer
Definition: ci.c:668
Definition: ci.h:77
virtual void Process(int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:1048
uint8_t capmt[2048]
XXX is there a specified maximum?
Definition: ci.c:761
cTimeMs alive
Definition: ci.c:421
cCiCaProgramData(int ProgramNumber)
Definition: ci.c:1670
#define RI_HOST_CONTROL
Definition: ci.c:469
#define AOT_DISPLAY_CONTROL
Definition: ci.c:493
#define MAXRECEIVEPIDS
Definition: receiver.h:15
int programNumber
Definition: ci.c:1667
unsigned char uchar
Definition: tools.h:30
#define EF_BLIND
Definition: ci.c:1102
int DeviceNumber(void) const
Returns the number of this device (0 ... numDevices - 1).
Definition: device.c:161
bool CanDecrypt(void)
Definition: ci.c:874
virtual void Action(void)
Handles the attached CAM slots in a separate thread.
Definition: ci.c:1718
int Vpid(void) const
Definition: channels.h:170
#define T_SB
Definition: ci.c:294
const int * Dpids(void) const
Definition: channels.h:174
bool Process(cTPDU *TPDU=NULL)
Definition: ci.c:1560
cChannels Channels
Definition: channels.c:864
#define AOT_PROFILE_ENQ
Definition: ci.c:476
#define dsyslog(a...)
Definition: tools.h:36
int pid
Definition: ci.c:1653
Definition: ci.h:296
uint16_t sessionId
Definition: ci.c:526
#define CA_ENCRYPTED_MIN
Definition: channels.h:49
cCiCaPidData(int Pid, int StreamType)
Definition: ci.c:1655
virtual bool ProvidesCa(const int *CaSystemIds)
Returns true if the CAM in this slot provides one of the given CaSystemIds.
Definition: ci.c:2089
static char * CopyString(int Length, const uint8_t *Data)
Definition: ci.c:75
void Set(int Ms=0)
Definition: tools.c:738
#define MAXCASYSTEMIDS
Definition: ci.c:737
virtual ~cCiAdapter()
The derived class must call Cancel(3) in its destructor.
Definition: ci.c:1686
virtual bool HasUserIO(void)
Definition: ci.c:1118
virtual void StartActivation(void)
Puts the CAM in this slot into a mode where an inserted smart card can be activated.
Definition: ci.c:1919
cCamSlot * ItCamSlot(int &Iter)
Iterates over all added CAM slots of this adapter.
Definition: ci.c:1707
#define MAX_TPDU_SIZE
Definition: ci.c:289
#define T_CREATE_TC
Definition: ci.c:296
cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc)
Definition: ci.c:543
cList< cCiCaPidData > pidList
Definition: ci.c:1669
virtual void AddPid(int ProgramNumber, int Pid, int StreamType)
Adds the given PID information to the list of PIDs.
Definition: ci.c:2104
bool HasUpdate(void)
Definition: ci.c:1329
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:2014
int esInfoLengthPos
Definition: ci.c:760
#define AOT_PROFILE_CHANGE
Definition: ci.c:478
bool CamChecked(int CamSlotNumber)
Definition: ci.c:2293
cCiMenu * menu
Definition: ci.c:1112
#define T_REQUEST_TC
Definition: ci.c:300
cTPDU(void)
Definition: ci.c:312
#define AOT_NONE
Definition: ci.c:475
#define CATPID
Definition: remux.h:53
cCiApplicationInformation(uint16_t SessionId, cCiTransportConnection *Tc)
Definition: ci.c:679
const char * GetMenuString(void)
Definition: ci.c:676
int Ca(int Index=0) const
Definition: channels.h:191
virtual void StartDecrypting(void)
Triggers sending all currently active CA_PMT entries to the CAM, so that it will start decrypting...
Definition: ci.c:2202
int caSystemIds[MAXCASYSTEMIDS+1]
Definition: ci.c:863
bool TsPayloadStart(const uchar *p)
Definition: remux.h:76
#define AOT_ENTER_MENU
Definition: ci.c:481
#define MAX_CONNECTIONS_PER_CAM_SLOT
Definition: ci.h:20
static char * GetString(int &Length, const uint8_t **Data)
Definition: ci.c:93
#define MAX_CAM_SLOTS_PER_ADAPTER
Definition: ci.h:19
#define AOT_APPLICATION_INFO_ENQ
Definition: ci.c:479
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1080
#define CHANNEL_CAM_RELATIONS_CLEANUP_INTERVAL
Definition: ci.c:2333
virtual void Append(T Data)
Definition: tools.h:571
Definition: ci.h:54
int QueueMessage(eMessageType Type, const char *s, int Seconds=0, int Timeout=0)
Like Message(), but this function may be called from a background thread.
Definition: skins.c:293
virtual void Process(int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:886
void SetCamSlot(cCamSlot *CamSlot)
Sets the given CamSlot to be used with this device.
Definition: device.c:361
#define T_CTC_REPLY
Definition: ci.c:297
char * bottomText
Definition: ci.h:35
#define SIZE_INDICATOR
Definition: ci.c:38
static bool DumpDateTime
Definition: ci.c:32
bool SendCloseMMI(void)
Definition: ci.c:1290
#define esyslog(a...)
Definition: tools.h:34
#define QUERY_WAIT_TIME
Definition: ci.c:856
#define dbgprotocol(a...)
Definition: ci.c:34
void Select(int Index)
Definition: ci.c:1335
virtual void Receive(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:163
#define AOT_ANSW
Definition: ci.c:500
#define ST_CLOSE_SESSION_RESPONSE
Definition: ci.c:457
virtual void Process(int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:618
void Detach(cFilter *Filter)
Detaches the given filter from this device.
Definition: device.c:616
Definition: ci.h:77
bool AttachReceiver(cReceiver *Receiver)
Attaches the given receiver to this device.
Definition: device.c:1663
virtual bool CanActivate(void)
Returns true if there is a CAM in this slot that can be put into activation mode. ...
Definition: ci.c:1914
static bool DumpTPDUDataTransfer
Definition: ci.c:29
#define DCC_SET_MMI_MODE
Definition: ci.c:1077
uint8_t ListManagement(void)
Definition: ci.c:771
#define AOT_CLOSE_MMI
Definition: ci.c:492
int source
Definition: ci.c:762
cTimeMs timer
Definition: ci.c:422
int GetTag(int &Length, const uint8_t **Data)
Definition: ci.c:554
#define CPCI_NOT_SELECTED
Definition: ci.c:753
virtual void Process(int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:1164
uint8_t tcid
Definition: ci.c:416
#define CPLM_ADD
Definition: ci.c:745
#define AOT_APPLICATION_INFO
Definition: ci.c:480
cCiMMI * mmi
Definition: ci.h:58
const int * GetCaSystemIds(void)
Definition: ci.c:869
cDevice * Device(void)
Definition: receiver.h:27
#define STREAM_TYPE_VIDEO
Definition: ci.c:2139
virtual ~cCaPidReceiver()
Definition: ci.c:127
uint32_t resourceId
Definition: ci.c:527
#define MINPRIORITY
Definition: config.h:40
#define AI_CANCEL
Definition: ci.c:1106
cCiSession * GetSessionByResourceId(uint32_t ResourceId)
Definition: ci.c:1480
char * GetText(int &Length, const uint8_t **Data)
Definition: ci.c:1148
cCiAdapter(void)
Definition: ci.c:1679
Definition: ci.c:1109
int SlotIndex(void)
Returns the index of this CAM slot within its CI adapter.
Definition: ci.h:178
cCamSlot * camSlot
Definition: ci.c:415
T min(T a, T b)
Definition: tools.h:54
int slotIndex
Definition: ci.h:138
cCaPidReceiver(void)
Definition: ci.c:133
int Transponder(void) const
Returns the transponder frequency in MHz, plus the polarization in case of sat.
Definition: channels.c:158
#define ST_SESSION_NUMBER
Definition: ci.c:451
const uint8_t * GetData(const uint8_t *Data, int &Length)
Definition: ci.c:389
bool CamDecrypt(int CamSlotNumber)
Definition: ci.c:2302
virtual bool HasUserIO(void)
Definition: ci.c:539
void NewConnection(void)
Definition: ci.c:1802
virtual eModuleStatus ModuleStatus(void)
Returns the status of the CAM in this slot.
Definition: ci.c:1945
int NumPids(void) const
Definition: receiver.h:76
void AddPid(int Pid, uint8_t StreamType)
Definition: ci.c:805
virtual void AddChannel(const cChannel *Channel)
Adds all PIDs if the given Channel to the current list of PIDs.
Definition: ci.c:2143
uint32_t ResourceId(void)
Definition: ci.c:538
void Write(cTPDU *TPDU)
Definition: ci.c:1887
void SetListManagement(uint8_t ListManagement)
Definition: ci.c:800
uint8_t Tcid(void) const
Definition: ci.c:436
#define MALLOC(type, size)
Definition: tools.h:46
#define RI_CONDITIONAL_ACCESS_SUPPORT
Definition: ci.c:468
void Cleanup(void)
Definition: ci.c:2342
int TsPid(const uchar *p)
Definition: remux.h:86
void SendCaPmt(uint8_t CmdId)
Definition: ci.c:2015
#define AOT_CA_INFO_ENQ
Definition: ci.c:482
uint32_t camSlotsChecked
Definition: ci.c:2265
void Reset(void)
Definition: ci.c:130
virtual bool Reset(int Slot)
Resets the CAM in the given Slot.
Definition: ci.h:101
#define AOT_TEXT_LAST
Definition: ci.c:495
uint8_t applicationType
Definition: ci.c:667
void CreateConnection(void)
Definition: ci.c:437
void SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId=0, int Status=-1)
Definition: ci.c:1442
T get_unaligned(T *p)
Definition: tools.h:72
#define MM_HIGH_LEVEL
Definition: ci.c:1085
bool blind
Definition: ci.h:61
#define AOT_MENU_LAST
Definition: ci.c:501
void AddEmmPid(int Pid)
Definition: ci.c:141
char * titleText
Definition: ci.h:33
#define MODULE_RESET_TIMEOUT
Definition: ci.c:1740
#define MAX_DUMP
#define IDLEPRIORITY
Definition: config.h:43
bool HasUserIO(void)
Definition: ci.c:441
static int CurrentChannel(void)
Returns the number of the current channel on the primary device.
Definition: device.h:323
#define ST_CLOSE_SESSION_REQUEST
Definition: ci.c:456
cCiTransportConnection * tc
Definition: ci.c:528
int streamType
Definition: ci.c:1654
T * Next(const T *object) const
Definition: tools.h:495
cCiSession * GetSessionByResourceId(uint32_t ResourceId)
Definition: ci.c:1881
int Source(void) const
Definition: channels.h:168
~cCiMenu()
Definition: ci.c:1308
virtual ~cCamSlot()
Definition: ci.c:1762
cChannelCamRelation(tChannelID ChannelID)
Definition: ci.c:2280
uint8_t Tag(void)
Definition: ci.c:316
int length
Definition: ci.c:120
char * subTitleText
Definition: ci.h:34
time_t lastScrambledTime
Definition: ci.c:251
uint32_t ResourceIdToInt(const uint8_t *Data)
Definition: ci.c:1470
bool Ready(void)
Definition: ci.c:1416
int programNumber
Definition: ci.c:764
#define CPLM_UPDATE
Definition: ci.c:746
#define QUERY_REPLY_WAIT
Definition: ci.c:2163
void HandleSessions(cTPDU *TPDU)
Definition: ci.c:1533
cCiEnquiry(cCiMMI *MMI)
Definition: ci.c:1356
void Process(cTPDU *TPDU=NULL)
Definition: ci.c:1824
#define MAX_TPDU_DATA
Definition: ci.c:290
virtual bool HasUserIO(void)
Returns true if there is a pending user interaction, which shall be retrieved via GetMenu() or GetEnq...
Definition: ci.c:1976
void DelEmmPids(void)
Definition: ci.c:151
uint16_t SessionId(void)
Definition: ci.c:537
void SetDecrypt(tChannelID ChannelID, int CamSlotNumber)
Definition: ci.c:2407
virtual bool CanDecrypt(const cChannel *Channel)
Returns true if there is a CAM in this slot that is able to decrypt the given Channel (or at least cl...
Definition: ci.c:2165
void SendData(int Tag, int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:575
cCiEnquiry * enquiry
Definition: ci.c:1113
cListObject * Next(void) const
Definition: tools.h:468
cCiSession * GetSessionBySessionId(uint16_t SessionId)
Definition: ci.c:1475
void SendDateTime(void)
Definition: ci.c:1023
virtual cCiMenu * GetMenu(void)
Gets a pending menu, or NULL if there is no menu.
Definition: ci.c:1989
bool SendAnswer(const char *Text)
Definition: ci.c:1278
cCiConditionalAccessSupport(uint16_t SessionId, cCiTransportConnection *Tc)
Definition: ci.c:877
#define AI_ANSWER
Definition: ci.c:1107
Definition: ci.c:524
int expectedLength
Definition: ci.h:62
bool TimedOut(void) const
Definition: tools.c:743
#define ST_OPEN_SESSION_RESPONSE
Definition: ci.c:453
int Tpid(void) const
Definition: channels.h:187
uint8_t Tcid(void)
Definition: ci.c:315
#define RI_MMI
Definition: ci.c:471
int size
Definition: ci.c:308
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition: thread.c:57
void Cancel(void)
Definition: ci.c:1342
bool EnterMenu(void)
Definition: ci.c:725
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, int EsPid)
Gets all CA descriptors for a given channel.
Definition: pat.c:278
#define STREAM_TYPE_AUDIO
Definition: ci.c:2140
cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds)
Definition: ci.c:775
#define AOT_CA_INFO
Definition: ci.c:483
#define AOT_PROFILE
Definition: ci.c:477
Definition: ci.h:79
#define ST_OPEN_SESSION_REQUEST
Definition: ci.c:452
uint8_t Slot(void)
Definition: ci.c:314
virtual ~cCiTransportConnection()
Definition: ci.c:1410
#define CA_ENABLE(x)
Definition: ci.c:854
cCiEnquiry * Enquiry(bool Clear=false)
Definition: ci.c:1261
#define AOT_LIST_LAST
Definition: ci.c:504
Definition: skins.h:24
#define AOT_DISPLAY_REPLY
Definition: ci.c:494
void ClrChecked(int CamSlotNumber)
Definition: ci.c:2320
cCamSlot(cCiAdapter *CiAdapter, bool WantsTsData=false)
Creates a new CAM slot for the given CiAdapter.
Definition: ci.c:1742
int Size(void)
Definition: ci.c:320
bool CamDecrypt(tChannelID ChannelID, int CamSlotNumber)
Definition: ci.c:2392
cSetup Setup
Definition: config.c:373
virtual int Read(uint8_t *Buffer, int MaxLength)
Reads one chunk of data into the given Buffer, up to MaxLength bytes.
Definition: ci.h:94
uint16_t manufacturerCode
Definition: ci.c:669
#define MAX_SESSIONS_PER_TC
Definition: ci.c:410
#define STREAM_TYPE_PRIVATE
Definition: ci.c:2141
Definition: ci.h:128
#define CPLM_ONLY
Definition: ci.c:744
uint32_t camSlotsDecrypt
Definition: ci.c:2266
void AddCamSlot(cCamSlot *CamSlot)
Adds the given CamSlot to this CI adapter.
Definition: ci.c:1693
cVector< int > emmPids
Definition: ci.c:117
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition: thread.h:99
cCiTransportConnection * Tc(void)
Definition: ci.c:533
bool active
Definition: ci.c:1652
uint8_t CmdId(void)
Definition: ci.c:769
#define AOT_ENQ
Definition: ci.c:499
Definition: ci.c:306
void OpenSession(int Length, const uint8_t *Data)
Definition: ci.c:1489
#define DATA_INDICATOR
Definition: ci.c:292
const int * GetCaSystemIds(void)
Definition: ci.c:2076
uint8_t Status(void)
Definition: ci.c:401
int Size(void) const
Definition: tools.h:551
bool HasCaPids(void)
Definition: ci.c:129
void SetChecked(int CamSlotNumber)
Definition: ci.c:2307
#define CPCI_OK_DESCRAMBLING
Definition: ci.c:750
void Dump(int SlotNumber, bool Outgoing)
Definition: ci.c:372
#define T_TC_ERROR
Definition: ci.c:302
bool AddPid(int Pid)
Adds the given Pid to the list of PIDs of this receiver.
Definition: receiver.c:37
#define AOT_MENU_ANSW
Definition: ci.c:503
bool Ready(void)
Definition: ci.c:872
cMutex mutex
Definition: thread.h:84
#define RI_DATE_TIME
Definition: ci.c:470
static uint8_t * SetLength(uint8_t *Data, int Length)
Definition: ci.c:55
virtual bool Reset(void)
Resets the CAM in this slot.
Definition: ci.c:1896
time_t lastTime
Definition: ci.c:1008
virtual bool IsDecrypting(void)
Returns true if the CAM in this slot is currently used for decrypting.
Definition: ci.c:2216
bool createConnectionRequested
Definition: ci.c:418
#define SS_OK
Definition: ci.c:461
cCiMMI(uint16_t SessionId, cCiTransportConnection *Tc)
Definition: ci.c:1126
void SendMenuAnswer(uint8_t Selection)
Definition: ci.c:1272
void SendData(int Length, const uint8_t *Data)
Definition: ci.c:1435
int Priority(void) const
Returns the priority of the current receiving session (-MAXPRIORITY..MAXPRIORITY), or IDLEPRIORITY if no receiver is currently active.
Definition: device.c:1564
const char * GetCamName(void)
Definition: ci.c:1422
#define T_RCV
Definition: ci.c:295
void DelPid(int Pid)
Deletes the given Pid from the list of PIDs of this receiver.
Definition: receiver.c:81
void ClrDecrypt(tChannelID ChannelID, int CamSlotNumber)
Definition: ci.c:2423
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:2122
cCamSlot * camSlots[MAX_CAM_SLOTS_PER_ADAPTER]
Definition: ci.h:82
const int * Apids(void) const
Definition: channels.h:173
virtual cCiEnquiry * GetEnquiry(void)
Gets a pending enquiry, or NULL if there is no enquiry.
Definition: ci.c:2002
cChannelCamRelation * AddEntry(tChannelID ChannelID)
Definition: ci.c:2367
cCiDateTime(uint16_t SessionId, cCiTransportConnection *Tc)
Definition: ci.c:1015
#define AOT_DATE_TIME_ENQ
Definition: ci.c:490
#define TC_ALIVE_TIMEOUT
Definition: ci.c:1394
uint8_t * Buffer(void)
Definition: ci.c:319
#define T_NEW_TC
Definition: ci.c:301
time_t lastChecked
Definition: ci.c:2267
bool WaitForAllCamSlotsReady(int Timeout=0)
Waits until all CAM slots have become ready, or the given Timeout (seconds) has expired.
Definition: ci.c:2242
cCiMenu * fetchedMenu
Definition: ci.c:1112
T * First(void) const
Definition: tools.h:492
#define CAM_CHECKED_TIMEOUT
Definition: ci.c:2260
bool modified
Definition: ci.c:1668
virtual ~cCiMMI()
Definition: ci.c:1134
void Del(cListObject *Object, bool DeleteObject=true)
Definition: tools.c:2046
~cCiEnquiry()
Definition: ci.c:1364
#define RI_RESOURCE_MANAGER
Definition: ci.c:466
virtual void Process(int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:597
cChannel * GetByNumber(int Number, int SkipGap=0)
Definition: channels.c:995
#define DRI_MMI_MODE_ACK
Definition: ci.c:1091
void SendPMT(cCiCaPmt *CaPmt)
Definition: ci.c:994
void Reply(const char *s)
Definition: ci.c:1372
#define T_DTC_REPLY
Definition: ci.c:299
cCiMenu * Menu(bool Clear=false)
Definition: ci.c:1250
Definition: ci.h:25
bool Active(void)
Checks whether the thread is still alive.
Definition: thread.c:298
uchar * bufp
Definition: ci.c:119
#define SS_NOT_ALLOCATED
Definition: ci.c:462
virtual eModuleStatus ModuleStatus(int Slot)
Returns the status of the CAM in the given Slot.
Definition: ci.h:104
virtual bool HasMMI(void)
Returns &#39;true&#39; if the CAM in this slot has an active MMI.
Definition: ci.c:1971
virtual ~cCaActivationReceiver()
Definition: ci.c:268
cCaActivationReceiver(const cChannel *Channel, cCamSlot *CamSlot)
Definition: ci.c:260
#define MODULE_CHECK_INTERVAL
Definition: ci.c:1739
void Cancel(void)
Definition: ci.c:1379
tChannelID ChannelID(void)
Definition: ci.c:2271
void SetChecked(tChannelID ChannelID, int CamSlotNumber)
Definition: ci.c:2399
#define T_DATA_LAST
Definition: ci.c:303
uint8_t cmdId
Definition: ci.c:758
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:1959
#define tr(s)
Definition: i18n.h:85
bool TimedOut(void)
Definition: ci.c:2288
cCiEnquiry * fetchedEnquiry
Definition: ci.c:1113
void ClrDecrypt(int CamSlotNumber)
Definition: ci.c:2326
bool TsIsScrambled(const uchar *p)
Definition: remux.h:91
cChannelCamRelations ChannelCamRelations
Definition: ci.c:2335
virtual bool Ready(void)
Returns &#39;true&#39; if the CAM in this slot is ready to decrypt.
Definition: ci.c:1965
void DeleteConnection(void)
Definition: ci.c:438
cCamSlot * camSlot
Definition: ci.c:250
#define T_DELETE_TC
Definition: ci.c:298
cMutex * mutex
Definition: ci.h:31
virtual void CancelActivation(void)
Cancels a previously started activation (if any).
Definition: ci.c:1933
#define isyslog(a...)
Definition: tools.h:35
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:2232
Definition: thread.h:77
Definition: ci.c:755
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:2083
bool CamChecked(tChannelID ChannelID, int CamSlotNumber)
Definition: ci.c:2385
virtual void StopDecrypting(void)
Clears the list of CA_PMT entries and tells the CAM to stop decrypting.
Definition: ci.c:2207
virtual ~cCiSession()
Definition: ci.c:550
void ClrChecked(tChannelID ChannelID, int CamSlotNumber)
Definition: ci.c:2415
#define TS_PACKET_FACTOR
Definition: ci.c:246
void AddCaDescriptors(int Length, const uint8_t *Data)
Definition: ci.c:821
const uint8_t * GetData(const uint8_t *Data, int &Length)
Definition: ci.c:569
void put_unaligned(unsigned int v, T *p)
Definition: tools.h:78
Definition: tools.h:333
void Abort(void)
Definition: ci.c:1384
#define TS_SIZE
Definition: remux.h:34
void Reset(int CamSlotNumber)
Definition: ci.c:2376
const int * Spids(void) const
Definition: channels.h:175
#define T_DATA_MORE
Definition: ci.c:304
cCiTransportConnection(cCamSlot *CamSlot, uint8_t Tcid)
Definition: ci.c:1396
virtual bool EnterMenu(void)
Requests the CAM in this slot to start its menu.
Definition: ci.c:1982
eModuleStatus
Definition: ci.h:77
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:283
int SupportTeletext
Definition: config.h:288
cCiMenu(cCiMMI *MMI, bool Selectable)
Definition: ci.c:1299
tChannelID channelID
Definition: ci.c:2264
#define TC_POLL_TIMEOUT
Definition: ci.c:1393
#define ST_CREATE_SESSION_RESPONSE
Definition: ci.c:455
#define QUERY_REPLY_TIMEOUT
Definition: ci.c:857
static const uint8_t * GetLength(const uint8_t *Data, int &Length)
Definition: ci.c:40
cCiMMI * mmi
Definition: ci.h:30
#define DEC2BCD(d)
#define AOT_DATE_TIME
Definition: ci.c:491
bool deleteConnectionRequested
Definition: ci.c:419
void CloseSession(uint16_t SessionId)
Definition: ci.c:1518
int Sid(void) const
Definition: channels.h:194
void Abort(void)
Definition: ci.c:1347
cChannelCamRelation * GetEntry(tChannelID ChannelID)
Definition: ci.c:2356
#define RI_APPLICATION_INFORMATION
Definition: ci.c:467
virtual void Clear(void)
Definition: tools.h:602
bool Assign(cDevice *Device, bool Query=false)
Assigns this CAM slot to the given Device, if this is possible.
Definition: ci.c:1772
#define AOT_CA_PMT
Definition: ci.c:484
uchar buffer[2048]
Definition: ci.c:118
Definition: ci.h:77
virtual void Activate(bool On)
This function is called just before the cReceiver gets attached to (On == true) and right after it ge...
Definition: ci.c:158
virtual bool IsActivating(void)
Returns true if this CAM slot is currently activating a smart card.
Definition: ci.c:1940
void SendTPDU(uint8_t Tag, int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:1428
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting &#39;running&#39; to false, so that the Action() loop can finish in an or...
Definition: thread.c:323
void Poll(void)
Definition: ci.c:1460
char * text
Definition: ci.h:60
cCamSlot * CamSlot(void)
Definition: ci.c:435
#define UNSCRAMBLE_TIME
Definition: ci.c:245
bool ReceivedReply(void)
Definition: ci.c:873
cMutex * mutex
Definition: ci.h:59
char * strreplace(char *s, char c1, char c2)
Definition: tools.c:139
cChannelCamRelations(void)
Definition: ci.c:2337
void DeleteAllConnections(void)
Definition: ci.c:1815
void Detach(void)
Definition: receiver.c:114
bool RepliesToQuery(void)
Definition: ci.c:871
virtual void Process(int Length=0, const uint8_t *Data=NULL)
Definition: ci.c:692
int transponder
Definition: ci.c:763
const uint8_t * Data(int &Length)
Definition: ci.c:317
int MaxSize(void)
Definition: ci.c:322
#define AOT_CA_PMT_REPLY
Definition: ci.c:485
cCamSlots CamSlots
Definition: ci.c:2240
int length
Definition: ci.c:759
int SlotNumber(void)
Returns the number of this CAM slot within the whole system.
Definition: ci.h:181
#define CAEI_POSSIBLE
Definition: ci.c:846
The cDevice class is the base from which actual devices can be derived.
Definition: device.h:109
int interval
Definition: ci.c:1007
Definition: tools.h:168
virtual ~cCiApplicationInformation()
Definition: ci.c:687
static bool DumpPolls
Definition: ci.c:31
static bool DebugProtocol
Definition: ci.c:30
virtual void Receive(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:273
cCiResourceManager(uint16_t SessionId, cCiTransportConnection *Tc)
Definition: ci.c:611
bool AddEntry(char *s)
Definition: ci.c:1320
int catVersion
Definition: ci.c:116
Definition: ci.h:77
cSkins Skins
Definition: skins.c:219
#define CPCI_QUERY
Definition: ci.c:752
void SetSize(int Size)
Definition: ci.c:321
void SetDecrypt(int CamSlotNumber)
Definition: ci.c:2314