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