MythTV  master
dvbci.cpp
Go to the documentation of this file.
1 /*
2  * ci.cc: Common Interface
3  *
4  * Copyright (C) 2000 Klaus Schmidinger
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
20  *
21  * The author can be reached at kls@cadsoft.de
22  *
23  * The project's page is at http://www.cadsoft.de/people/kls/vdr
24  *
25  */
26 
27 #include "dvbci.h"
28 
29 #include <cctype>
30 #include <cerrno>
31 #include <cstring>
32 #include <ctime>
33 #include <fcntl.h>
34 #include <linux/dvb/ca.h>
35 #include <netinet/in.h>
36 #include <poll.h>
37 #include <sys/ioctl.h>
38 #include <sys/time.h>
39 #include <unistd.h>
40 #ifdef __FreeBSD__
41 # include <stdlib.h>
42 #else
43 # include <malloc.h>
44 #endif
45 
46 #include <QString>
47 
48 #include "mythlogging.h"
49 
50 #ifndef MALLOC
51 #define MALLOC(type, size) (type *)malloc(sizeof(type) * (size))
52 #endif
53 
54 #define esyslog(a...) LOG(VB_GENERAL, LOG_ERR, QString().sprintf(a))
55 #define isyslog(a...) LOG(VB_DVBCAM, LOG_INFO, QString().sprintf(a))
56 #define dsyslog(a...) LOG(VB_DVBCAM, LOG_DEBUG, QString().sprintf(a))
57 
58 #define LOG_ERROR esyslog("ERROR (%s,%d): %m", __FILE__, __LINE__)
59 #define LOG_ERROR_STR(s) esyslog("ERROR: %s: %m", s)
60 
61 
62 // Set these to 'true' for debug output:
63 static bool DumpTPDUDataTransfer = false;
64 static bool DebugProtocol = false;
65 static bool _connected = false;
66 
67 #define dbgprotocol(a...) if (DebugProtocol) LOG(VB_DVBCAM, LOG_DEBUG, QString().sprintf(a))
68 
69 #define OK 0
70 #define TIMEOUT (-1)
71 #define ERROR (-2)
72 
73 // --- Workarounds -----------------------------------------------------------
74 
75 // The Irdeto AllCAM 4.7 (and maybe others, too) does not react on AOT_ENTER_MENU
76 // during the first few seconds of a newly established connection
77 #define WRKRND_TIME_BEFORE_ENTER_MENU 15 // seconds
78 
79 // --- Helper functions ------------------------------------------------------
80 
81 #define SIZE_INDICATOR 0x80
82 
83 static ssize_t safe_read(int filedes, void *buffer, size_t size)
84 {
85  for (;;) {
86  ssize_t p = read(filedes, buffer, size);
87  if (p < 0 && (errno == EINTR || errno == EAGAIN)) {
88  dsyslog("EINTR while reading from file handle %d - retrying", filedes);
89  continue;
90  }
91  return p;
92  }
93 }
94 
95 static const uint8_t *GetLength(const uint8_t *Data, int &Length)
106 {
107  Length = *Data++;
108  if ((Length & SIZE_INDICATOR) != 0) {
109  int l = Length & ~SIZE_INDICATOR;
110  Length = 0;
111  for (int i = 0; i < l; i++)
112  Length = (Length << 8) | *Data++;
113  }
114  return Data;
115 }
116 
117 static uint8_t *SetLength(uint8_t *Data, int Length)
127 {
128  uint8_t *p = Data;
129  if (Length < 128)
130  *p++ = Length;
131  else {
132  int n = sizeof(Length);
133  for (int i = n - 1; i >= 0; i--) {
134  int b = (Length >> (8 * i)) & 0xFF;
135  if (p != Data || b)
136  *++p = b;
137  }
138  *Data = (p - Data) | SIZE_INDICATOR;
139  p++;
140  }
141  return p;
142 }
143 
144 static char *CopyString(int Length, const uint8_t *Data)
149 {
150  char *s = MALLOC(char, Length + 1);
151  strncpy(s, (char *)Data, Length);
152  s[Length] = 0;
153  return s;
154 }
155 
156 static char *GetString(int &Length, const uint8_t **Data)
164 {
165  if (Length > 0 && Data && *Data) {
166  int l = 0;
167  const uint8_t *d = GetLength(*Data, l);
168  char *s = CopyString(l, d);
169  Length -= d - *Data + l;
170  *Data = d + l;
171  return s;
172  }
173  return nullptr;
174 }
175 
176 
177 
178 // --- cMutex ----------------------------------------------------------------
179 
181 {
182  lockingPid = 0;
183  locked = 0;
184  pthread_mutex_init(&mutex, nullptr);
185 }
186 
188 {
189  pthread_mutex_destroy(&mutex);
190 }
191 
192 void cMutex::Lock(void)
193 {
194  if (getpid() != lockingPid || !locked) {
195  pthread_mutex_lock(&mutex);
196  lockingPid = getpid();
197  }
198  locked++;
199 }
200 
201 void cMutex::Unlock(void)
202 {
203  if (--locked <= 0) {
204  if (locked < 0) {
205  esyslog("cMutex Lock inbalance detected");
206  locked = 0;
207  }
208  lockingPid = 0;
209  pthread_mutex_unlock(&mutex);
210  }
211 }
212 // --- cMutexLock ------------------------------------------------------------
213 
215 {
216  mutex = nullptr;
217  locked = false;
218  Lock(Mutex);
219 }
220 
222 {
223  if (mutex && locked)
224  mutex->Unlock();
225 }
226 
228 {
229  if (Mutex && !mutex) {
230  mutex = Mutex;
231  Mutex->Lock();
232  locked = true;
233  return true;
234  }
235  return false;
236 }
237 
238 
239 
240 // --- cTPDU -----------------------------------------------------------------
241 
242 #define MAX_TPDU_SIZE 2048
243 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
244 
245 #define DATA_INDICATOR 0x80
246 
247 #define T_SB 0x80
248 #define T_RCV 0x81
249 #define T_CREATE_TC 0x82
250 #define T_CTC_REPLY 0x83
251 #define T_DELETE_TC 0x84
252 #define T_DTC_REPLY 0x85
253 #define T_REQUEST_TC 0x86
254 #define T_NEW_TC 0x87
255 #define T_TC_ERROR 0x88
256 #define T_DATA_LAST 0xA0
257 #define T_DATA_MORE 0xA1
258 
259 class cTPDU {
260 private:
261  int size;
262  uint8_t data[MAX_TPDU_SIZE];
263  const uint8_t *GetData(const uint8_t *Data, int &Length);
264 public:
265  cTPDU(void) { size = 0; memset(data, 0, sizeof(uint8_t) * MAX_TPDU_SIZE); }
266  cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length = 0, const uint8_t *Data = nullptr);
267  uint8_t Slot(void) { return data[0]; }
268  uint8_t Tcid(void) { return data[1]; }
269  uint8_t Tag(void) { return data[2]; }
270  const uint8_t *Data(int &Length) { return GetData(data + 3, Length); }
271  uint8_t Status(void);
272  int Write(int fd);
273  int Read(int fd);
274  void Dump(bool Outgoing);
275  };
276 
277 cTPDU::cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length, const uint8_t *Data)
278 {
279  size = 0;
280  data[0] = Slot;
281  data[1] = Tcid;
282  data[2] = Tag;
283  switch (Tag) {
284  case T_RCV:
285  case T_CREATE_TC:
286  case T_CTC_REPLY:
287  case T_DELETE_TC:
288  case T_DTC_REPLY:
289  case T_REQUEST_TC:
290  data[3] = 1; // length
291  data[4] = Tcid;
292  size = 5;
293  break;
294  case T_NEW_TC:
295  case T_TC_ERROR:
296  if (Length == 1) {
297  data[3] = 2; // length
298  data[4] = Tcid;
299  data[5] = Data[0];
300  size = 6;
301  }
302  else
303  esyslog("ERROR: illegal data length for TPDU tag 0x%02X: %d", Tag, Length);
304  break;
305  case T_DATA_LAST:
306  case T_DATA_MORE:
307  if (Length <= MAX_TPDU_DATA) {
308  uint8_t *p = data + 3;
309  p = SetLength(p, Length + 1);
310  *p++ = Tcid;
311  if (Length)
312  memcpy(p, Data, Length);
313  size = Length + (p - data);
314  }
315  else
316  esyslog("ERROR: illegal data length for TPDU tag 0x%02X: %d", Tag, Length);
317  break;
318  default:
319  esyslog("ERROR: unknown TPDU tag: 0x%02X", Tag);
320  }
321  }
322 
323 int cTPDU::Write(int fd)
324 {
325  Dump(true);
326  if (size)
327  return write(fd, data, size) == size ? OK : ERROR;
328  esyslog("ERROR: attemp to write TPDU with zero size");
329  return ERROR;
330 }
331 
332 int cTPDU::Read(int fd)
333 {
334  size = safe_read(fd, data, sizeof(data));
335  if (size < 0) {
336  esyslog("ERROR: %m");
337  size = 0;
338  return ERROR;
339  }
340  Dump(false);
341  return OK;
342 }
343 
344 void cTPDU::Dump(bool Outgoing)
345 {
346  if (DumpTPDUDataTransfer) {
347 #define MAX_DUMP 256
348  QString msg = QString("%1 ").arg(Outgoing ? "-->" : "<--");
349  for (int i = 0; i < size && i < MAX_DUMP; i++)
350  msg += QString("%1 ").arg((short int)data[i], 2, 16, QChar('0'));
351  if (size >= MAX_DUMP)
352  msg += "...";
353  LOG(VB_DVBCAM, LOG_INFO, msg);
354  if (!Outgoing) {
355  msg = QString(" ");
356  for (int i = 0; i < size && i < MAX_DUMP; i++)
357  msg += QString("%1 ").arg(isprint(data[i]) ? data[i] : '.', 2);
358  if (size >= MAX_DUMP)
359  msg += "...";
360  LOG(VB_DVBCAM, LOG_INFO, msg);
361  }
362  }
363 }
364 
365 const uint8_t *cTPDU::GetData(const uint8_t *Data, int &Length)
366 {
367  if (size) {
368  Data = GetLength(Data, Length);
369  if (Length) {
370  Length--; // the first byte is always the tcid
371  return Data + 1;
372  }
373  }
374  return nullptr;
375 }
376 
377 uint8_t cTPDU::Status(void)
378 {
379  if (size >= 4 && data[size - 4] == T_SB && data[size - 3] == 2) {
380  //XXX test tcid???
381  return data[size - 1];
382  }
383  return 0;
384 }
385 
386 // --- cCiTransportConnection ------------------------------------------------
387 
389 
391  friend class cCiTransportLayer;
392 private:
393  int fd;
394  uint8_t slot;
395  uint8_t tcid;
398  struct timeval last_poll;
401  void Init(int Fd, uint8_t Slot, uint8_t Tcid);
402  int SendTPDU(uint8_t Tag, int Length = 0, const uint8_t *Data = nullptr);
403  int RecvTPDU(void);
404  int CreateConnection(void);
405  int Poll(void);
406  eState State(void) { return state; }
407  int LastResponse(void) { return lastResponse; }
408  bool DataAvailable(void) { return dataAvailable; }
409 public:
412  int Slot(void) const { return slot; }
413  int SendData(int Length, const uint8_t *Data);
414  int RecvData(void);
415  const uint8_t *Data(int &Length);
416  //XXX Close()
417  };
418 
420 {
421  tpdu = nullptr;
422  last_poll.tv_sec = 0;
423  last_poll.tv_usec = 0;
424  Init(-1, 0, 0);
425 }
426 
428 {
429  delete tpdu;
430 }
431 
432 void cCiTransportConnection::Init(int Fd, uint8_t Slot, uint8_t Tcid)
433 {
434  fd = Fd;
435  slot = Slot;
436  tcid = Tcid;
437  state = stIDLE;
438  if (fd >= 0 && !tpdu)
439  tpdu = new cTPDU;
441  dataAvailable = false;
442 //XXX Clear()???
443 }
444 
445 int cCiTransportConnection::SendTPDU(uint8_t Tag, int Length, const uint8_t *Data)
446 {
447  cTPDU TPDU(slot, tcid, Tag, Length, Data);
448  return TPDU.Write(fd);
449 }
450 
451 #define CAM_READ_TIMEOUT 5000 // ms
452 
454 {
455  struct pollfd pfd[1];
456  pfd[0].fd = fd;
457  pfd[0].events = POLLIN;
459 
460  for (;;) {
461  int ret = poll(pfd, 1, CAM_READ_TIMEOUT);
462  if (ret == -1 && (errno == EAGAIN || errno == EINTR))
463  continue;
464  break;
465  }
466 
467  if (
468  (pfd[0].revents & POLLIN) &&
469  tpdu->Read(fd) == OK &&
470  tpdu->Tcid() == tcid
471  )
472  {
473  switch (state) {
474  case stIDLE: break;
475  case stCREATION: if (tpdu->Tag() == T_CTC_REPLY) {
477  state = stACTIVE;
478  lastResponse = tpdu->Tag();
479  }
480  break;
481  case stACTIVE: switch (tpdu->Tag()) {
482  case T_SB:
483  case T_DATA_LAST:
484  case T_DATA_MORE:
485  case T_REQUEST_TC: break;
486  case T_DELETE_TC: if (SendTPDU(T_DTC_REPLY) != OK)
487  return ERROR;
488  Init(fd, slot, tcid);
489  break;
490  default: return ERROR;
491  }
493  lastResponse = tpdu->Tag();
494  break;
495  case stDELETION: if (tpdu->Tag() == T_DTC_REPLY) {
496  Init(fd, slot, tcid);
497  //XXX Status()???
498  lastResponse = tpdu->Tag();
499  }
500  break;
501  }
502  }
503  else {
504  esyslog("ERROR: CAM: Read failed: slot %d, tcid %d\n", slot, tcid);
505  Init(-1, slot, tcid);
506  }
507  return lastResponse;
508 }
509 
510 int cCiTransportConnection::SendData(int Length, const uint8_t *Data)
511 {
512  while (state == stACTIVE && Length > 0) {
513  uint8_t Tag = T_DATA_LAST;
514  int l = Length;
515  if (l > MAX_TPDU_DATA) {
516  Tag = T_DATA_MORE;
517  l = MAX_TPDU_DATA;
518  }
519  if (SendTPDU(Tag, l, Data) != OK || RecvTPDU() != T_SB)
520  break;
521  Length -= l;
522  Data += l;
523  }
524  return Length ? ERROR : OK;
525 }
526 
528 {
529  if (SendTPDU(T_RCV) == OK)
530  return RecvTPDU();
531  return ERROR;
532 }
533 
534 const uint8_t *cCiTransportConnection::Data(int &Length)
535 {
536  return tpdu->Data(Length);
537 }
538 
539 #define MAX_CONNECT_RETRIES 25
540 
542 {
543  if (state == stIDLE) {
544  if (SendTPDU(T_CREATE_TC) == OK) {
545  state = stCREATION;
546  if (RecvTPDU() == T_CTC_REPLY) {
547  _connected=true;
548  return OK;
549  // the following is a workaround for CAMs that don't quite follow the specs...
550  } else {
551  for (int i = 0; i < MAX_CONNECT_RETRIES; i++) {
552  dsyslog("CAM: retrying to establish connection");
553  if (RecvTPDU() == T_CTC_REPLY) {
554  dsyslog("CAM: connection established");
555  _connected=true;
556  return OK;
557  }
558  }
559  return ERROR;
560  }
561  }
562  }
563  return ERROR;
564 }
565 
566 // Polls can be done with a 100ms interval (EN50221 - A.4.1.12)
567 #define POLL_INTERVAL 100
568 
570 {
571  struct timeval curr_time;
572 
573  if (state != stACTIVE)
574  return ERROR;
575 
576  gettimeofday(&curr_time, nullptr);
577  uint64_t msdiff = (curr_time.tv_sec * 1000) + (curr_time.tv_usec / 1000) -
578  (last_poll.tv_sec * 1000) - (last_poll.tv_usec / 1000);
579 
580  if (msdiff < POLL_INTERVAL)
581  return OK;
582 
583  last_poll.tv_sec = curr_time.tv_sec;
584  last_poll.tv_usec = curr_time.tv_usec;
585 
586  if (SendTPDU(T_DATA_LAST) != OK)
587  return ERROR;
588 
589  return RecvTPDU();
590 }
591 
592 // --- cCiTransportLayer -----------------------------------------------------
593 
594 #define MAX_CI_CONNECT 16 // maximum possible value is 254
595 
597 private:
598  int fd;
599  int numSlots;
601 public:
602  cCiTransportLayer(int Fd, int NumSlots);
604  bool ResetSlot(int Slot);
605  bool ModuleReady(int Slot);
606  cCiTransportConnection *Process(int Slot);
607  };
608 
610 {
611  fd = Fd;
612  numSlots = NumSlots;
613  for (int s = 0; s < numSlots; s++)
614  ResetSlot(s);
615 }
616 
618 {
619  for (int i = 0; i < MAX_CI_CONNECT; i++) {
620  if (tc[i].State() == stIDLE) {
621  dbgprotocol("Creating connection: slot %d, tcid %d\n", Slot, i + 1);
622  tc[i].Init(fd, Slot, i + 1);
623  if (tc[i].CreateConnection() == OK)
624  return &tc[i];
625  break;
626  }
627  }
628  return nullptr;
629 }
630 
632 {
633  dbgprotocol("Resetting slot %d...", Slot);
634  if (ioctl(fd, CA_RESET, 1 << Slot) != -1) {
635  dbgprotocol("ok.\n");
636  return true;
637  }
638  else
639  esyslog("ERROR: can't reset CAM slot %d: %m", Slot);
640  dbgprotocol("failed!\n");
641  return false;
642 }
643 
645 {
646  ca_slot_info_t sinfo;
647  sinfo.num = Slot;
648  if (ioctl(fd, CA_GET_SLOT_INFO, &sinfo) != -1)
649  return sinfo.flags & CA_CI_MODULE_READY;
650  else
651  esyslog("ERROR: can't get info on CAM slot %d: %m", Slot);
652  return false;
653 }
654 
656 {
657  for (int i = 0; i < MAX_CI_CONNECT; i++) {
658  cCiTransportConnection *Tc = &tc[i];
659  if (Tc->Slot() == Slot) {
660  switch (Tc->State()) {
661  case stCREATION:
662  case stACTIVE:
663  if (!Tc->DataAvailable()) {
664  Tc->Poll();
665  }
666  switch (Tc->LastResponse()) {
667  case T_REQUEST_TC:
668  //XXX
669  break;
670  case T_DATA_MORE:
671  case T_DATA_LAST:
672  case T_CTC_REPLY:
673  case T_SB:
674  if (Tc->DataAvailable())
675  Tc->RecvData();
676  break;
677  case TIMEOUT:
678  case ERROR:
679  default:
680  //XXX Tc->state = stIDLE;//XXX Init()???
681  return nullptr;
682  break;
683  }
684  //XXX this will only work with _one_ transport connection per slot!
685  return Tc;
686  break;
687  default: ;
688  }
689  }
690  }
691  return nullptr;
692 }
693 
694 // -- cCiSession -------------------------------------------------------------
695 
696 // Session Tags:
697 
698 #define ST_SESSION_NUMBER 0x90
699 #define ST_OPEN_SESSION_REQUEST 0x91
700 #define ST_OPEN_SESSION_RESPONSE 0x92
701 #define ST_CREATE_SESSION 0x93
702 #define ST_CREATE_SESSION_RESPONSE 0x94
703 #define ST_CLOSE_SESSION_REQUEST 0x95
704 #define ST_CLOSE_SESSION_RESPONSE 0x96
705 
706 // Session Status:
707 
708 #define SS_OK 0x00
709 #define SS_NOT_ALLOCATED 0xF0
710 
711 // Resource Identifiers:
712 
713 #define RI_RESOURCE_MANAGER 0x00010041
714 #define RI_APPLICATION_INFORMATION 0x00020041
715 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
716 #define RI_HOST_CONTROL 0x00200041
717 #define RI_DATE_TIME 0x00240041
718 #define RI_MMI 0x00400041
719 
720 // Application Object Tags:
721 
722 #define AOT_NONE 0x000000
723 #define AOT_PROFILE_ENQ 0x9F8010
724 #define AOT_PROFILE 0x9F8011
725 #define AOT_PROFILE_CHANGE 0x9F8012
726 #define AOT_APPLICATION_INFO_ENQ 0x9F8020
727 #define AOT_APPLICATION_INFO 0x9F8021
728 #define AOT_ENTER_MENU 0x9F8022
729 #define AOT_CA_INFO_ENQ 0x9F8030
730 #define AOT_CA_INFO 0x9F8031
731 #define AOT_CA_PMT 0x9F8032
732 #define AOT_CA_PMT_REPLY 0x9F8033
733 #define AOT_TUNE 0x9F8400
734 #define AOT_REPLACE 0x9F8401
735 #define AOT_CLEAR_REPLACE 0x9F8402
736 #define AOT_ASK_RELEASE 0x9F8403
737 #define AOT_DATE_TIME_ENQ 0x9F8440
738 #define AOT_DATE_TIME 0x9F8441
739 #define AOT_CLOSE_MMI 0x9F8800
740 #define AOT_DISPLAY_CONTROL 0x9F8801
741 #define AOT_DISPLAY_REPLY 0x9F8802
742 #define AOT_TEXT_LAST 0x9F8803
743 #define AOT_TEXT_MORE 0x9F8804
744 #define AOT_KEYPAD_CONTROL 0x9F8805
745 #define AOT_KEYPRESS 0x9F8806
746 #define AOT_ENQ 0x9F8807
747 #define AOT_ANSW 0x9F8808
748 #define AOT_MENU_LAST 0x9F8809
749 #define AOT_MENU_MORE 0x9F880A
750 #define AOT_MENU_ANSW 0x9F880B
751 #define AOT_LIST_LAST 0x9F880C
752 #define AOT_LIST_MORE 0x9F880D
753 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
754 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
755 #define AOT_DISPLAY_MESSAGE 0x9F8810
756 #define AOT_SCENE_END_MARK 0x9F8811
757 #define AOT_SCENE_DONE 0x9F8812
758 #define AOT_SCENE_CONTROL 0x9F8813
759 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
760 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
761 #define AOT_FLUSH_DOWNLOAD 0x9F8816
762 #define AOT_DOWNLOAD_REPLY 0x9F8817
763 #define AOT_COMMS_CMD 0x9F8C00
764 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
765 #define AOT_COMMS_REPLY 0x9F8C02
766 #define AOT_COMMS_SEND_LAST 0x9F8C03
767 #define AOT_COMMS_SEND_MORE 0x9F8C04
768 #define AOT_COMMS_RCV_LAST 0x9F8C05
769 #define AOT_COMMS_RCV_MORE 0x9F8C06
770 
771 class cCiSession {
772 private:
776 protected:
777  int GetTag(int &Length, const uint8_t **Data);
778  const uint8_t *GetData(const uint8_t *Data, int &Length);
779  int SendData(int Tag, int Length = 0, const uint8_t *Data = nullptr);
780 public:
782  virtual ~cCiSession();
783  const cCiTransportConnection *Tc(void) { return tc; }
784  int SessionId(void) { return sessionId; }
785  int ResourceId(void) { return resourceId; }
786  virtual bool HasUserIO(void) { return false; }
787  virtual bool Process(int Length = 0, const uint8_t *Data = nullptr);
788  };
789 
790 cCiSession::cCiSession(int SessionId, int ResourceId, cCiTransportConnection *Tc)
791 {
794  tc = Tc;
795 }
796 
798 {
799 }
800 
801 int cCiSession::GetTag(int &Length, const uint8_t **Data)
809 {
810  if (Length >= 3 && Data && *Data) {
811  int t = 0;
812  for (int i = 0; i < 3; i++)
813  t = (t << 8) | *(*Data)++;
814  Length -= 3;
815  return t;
816  }
817  return AOT_NONE;
818 }
819 
820 const uint8_t *cCiSession::GetData(const uint8_t *Data, int &Length)
821 {
822  Data = GetLength(Data, Length);
823  return Length ? Data : nullptr;
824 }
825 
826 int cCiSession::SendData(int Tag, int Length, const uint8_t *Data)
827 {
828  uint8_t buffer[2048];
829  uint8_t *p = buffer;
830  *p++ = ST_SESSION_NUMBER;
831  *p++ = 0x02;
832  *p++ = (sessionId >> 8) & 0xFF;
833  *p++ = sessionId & 0xFF;
834  *p++ = (Tag >> 16) & 0xFF;
835  *p++ = (Tag >> 8) & 0xFF;
836  *p++ = Tag & 0xFF;
837  if (Length >= 0)
838  {
839  p = SetLength(p, Length);
840  if (p - buffer + Length < int(sizeof(buffer)))
841  {
842  if (Length != 0)
843  {
844  if (!Data)
845  {
846  esyslog("ERROR: CAM: Data pointer null");
847  return ERROR;
848  }
849  memcpy(p, Data, Length);
850  p += Length;
851  }
852  return tc->SendData(p - buffer, buffer);
853  }
854  esyslog("ERROR: CAM: data length (%d) exceeds buffer size", Length);
855  }
856  esyslog("ERROR: CAM: data length (%d) is negative", Length);
857  return ERROR;
858 }
859 
860 bool cCiSession::Process(int Length, const uint8_t *Data)
861 {
862  (void)Length;
863  (void)Data;
864  return true;
865 }
866 
867 // -- cCiResourceManager -----------------------------------------------------
868 
870 private:
871  int state;
872 public:
874  bool Process(int Length = 0, const uint8_t *Data = nullptr) override; // cCiSession
875  };
876 
878 :cCiSession(SessionId, RI_RESOURCE_MANAGER, Tc)
879 {
880  dbgprotocol("New Resource Manager (session id %d)\n", SessionId);
881  state = 0;
882 }
883 
884 bool cCiResourceManager::Process(int Length, const uint8_t *Data)
885 {
886  if (Data) {
887  int Tag = GetTag(Length, &Data);
888  switch (Tag) {
889  case AOT_PROFILE_ENQ: {
890  dbgprotocol("%d: <== Profile Enquiry\n", SessionId());
891  uint32_t resources[] =
892  {
893  htonl(RI_RESOURCE_MANAGER),
896  htonl(RI_DATE_TIME),
897  htonl(RI_MMI)
898  };
899  dbgprotocol("%d: ==> Profile\n", SessionId());
900  SendData(AOT_PROFILE, sizeof(resources), (uint8_t*)resources);
901  state = 3;
902  }
903  break;
904  case AOT_PROFILE: {
905  dbgprotocol("%d: <== Profile\n", SessionId());
906  if (state == 1) {
907  int l = 0;
908  const uint8_t *d = GetData(Data, l);
909  if (l > 0 && d)
910  esyslog("CI resource manager: unexpected data");
911  dbgprotocol("%d: ==> Profile Change\n", SessionId());
913  state = 2;
914  }
915  else {
916  esyslog("ERROR: CI resource manager: unexpected tag %06X in state %d", Tag, state);
917  }
918  }
919  break;
920  default: esyslog("ERROR: CI resource manager: unknown tag %06X", Tag);
921  return false;
922  }
923  }
924  else if (state == 0) {
925  dbgprotocol("%d: ==> Profile Enq\n", SessionId());
927  state = 1;
928  }
929  return true;
930 }
931 
932 // --- cCiApplicationInformation ---------------------------------------------
933 
935 private:
936  int state;
937  time_t creationTime;
941  char *menuString;
942 public:
944  virtual ~cCiApplicationInformation();
945  bool Process(int Length = 0, const uint8_t *Data = nullptr) override; // cCiSession
946  bool EnterMenu(void);
947  char *GetApplicationString() { return strdup(menuString); };
950  };
951 
953 :cCiSession(SessionId, RI_APPLICATION_INFORMATION, Tc)
954 {
955  dbgprotocol("New Application Information (session id %d)\n", SessionId);
956  state = 0;
957  creationTime = time(nullptr);
958  applicationType = 0;
960  manufacturerCode = 0;
961  menuString = nullptr;
962 }
963 
965 {
966  free(menuString);
967 }
968 
969 bool cCiApplicationInformation::Process(int Length, const uint8_t *Data)
970 {
971  if (Data) {
972  int Tag = GetTag(Length, &Data);
973  switch (Tag) {
974  case AOT_APPLICATION_INFO: {
975  dbgprotocol("%d: <== Application Info\n", SessionId());
976  int l = 0;
977  const uint8_t *d = GetData(Data, l);
978  if ((l -= 1) < 0) break;
979  applicationType = *d++;
980  if ((l -= 2) < 0) break;
981  applicationManufacturer = ntohs(*(uint16_t *)d);
982  d += 2;
983  if ((l -= 2) < 0) break;
984  manufacturerCode = ntohs(*(uint16_t *)d);
985  d += 2;
986  free(menuString);
987  menuString = GetString(l, &d);
988  isyslog("CAM: %s, %02X, %04X, %04X", menuString, applicationType,
990  }
991  state = 2;
992  break;
993  default: esyslog("ERROR: CI application information: unknown tag %06X", Tag);
994  return false;
995  }
996  }
997  else if (state == 0) {
998  dbgprotocol("%d: ==> Application Info Enq\n", SessionId());
1000  state = 1;
1001  }
1002  return true;
1003 }
1004 
1006 {
1007  if (state == 2 && time(nullptr) - creationTime > WRKRND_TIME_BEFORE_ENTER_MENU) {
1008  dbgprotocol("%d: ==> Enter Menu\n", SessionId());
1010  return true;//XXX
1011  }
1012  return false;
1013 }
1014 
1015 // --- cCiConditionalAccessSupport -------------------------------------------
1016 
1018 private:
1019  int state;
1021  unsigned short caSystemIds[MAXCASYSTEMIDS + 1]; // list is zero terminated!
1023 public:
1025  bool Process(int Length = 0, const uint8_t *Data = nullptr) override; // cCiSession
1026  const unsigned short *GetCaSystemIds(void) { return caSystemIds; }
1027  bool SendPMT(cCiCaPmt &CaPmt);
1028  bool NeedCaPmt(void) { return needCaPmt; }
1029  };
1030 
1032  int SessionId, cCiTransportConnection *Tc) :
1033  cCiSession(SessionId, RI_CONDITIONAL_ACCESS_SUPPORT, Tc),
1034  state(0), numCaSystemIds(0), needCaPmt(false)
1035 {
1036  dbgprotocol("New Conditional Access Support (session id %d)\n", SessionId);
1037  memset(caSystemIds, 0, sizeof(caSystemIds));
1038 }
1039 
1040 bool cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
1041 {
1042  if (Data) {
1043  int Tag = GetTag(Length, &Data);
1044  switch (Tag) {
1045  case AOT_CA_INFO: {
1046  dbgprotocol("%d: <== Ca Info", SessionId());
1047  int l = 0;
1048  const uint8_t *d = GetData(Data, l);
1049  while (l > 1) {
1050  unsigned short id = ((unsigned short)(*d) << 8) | *(d + 1);
1051  dbgprotocol(" %04X", id);
1052  d += 2;
1053  l -= 2;
1055  int i = 0;
1056  // Make sure the id is not already present
1057  for (; i < numCaSystemIds; i++)
1058  if (caSystemIds[i] == id)
1059  break;
1060 
1061  if (i < numCaSystemIds)
1062  continue;
1063 
1064  caSystemIds[numCaSystemIds++] = id;
1066  }
1067  else
1068  esyslog("ERROR: too many CA system IDs!");
1069  }
1070  dbgprotocol("\n");
1071  }
1072  state = 2;
1073  needCaPmt = true;
1074  break;
1075  default: esyslog("ERROR: CI conditional access support: unknown tag %06X", Tag);
1076  return false;
1077  }
1078  }
1079  else if (state == 0) {
1080  dbgprotocol("%d: ==> Ca Info Enq\n", SessionId());
1082  state = 1;
1083  }
1084  return true;
1085 }
1086 
1088 {
1089  if (state == 2) {
1090  SendData(AOT_CA_PMT, CaPmt.length, CaPmt.capmt);
1091  needCaPmt = false;
1092  return true;
1093  }
1094  return false;
1095 }
1096 
1097 // --- cCiDateTime -----------------------------------------------------------
1098 
1099 class cCiDateTime : public cCiSession {
1100 private:
1102  time_t lastTime;
1104  bool SendDateTime(void);
1105 public:
1107  bool Process(int Length = 0, const uint8_t *Data = nullptr) override; // cCiSession
1108  void SetTimeOffset(double offset);
1109  };
1110 
1112 :cCiSession(SessionId, RI_DATE_TIME, Tc)
1113 {
1114  interval = 0;
1115  lastTime = 0;
1116  timeOffset = 0;
1117  dbgprotocol("New Date Time (session id %d)\n", SessionId);
1118 }
1119 
1120 void cCiDateTime::SetTimeOffset(double offset)
1121 {
1122  timeOffset = (int) offset;
1123  dbgprotocol("New Time Offset: %i secs\n", timeOffset);
1124 }
1125 
1127 {
1128  time_t t = time(nullptr);
1129  struct tm tm_gmt;
1130  struct tm tm_loc;
1131 
1132  // Avoid using signed time_t types
1133  if (timeOffset < 0)
1134  t -= (time_t)(-timeOffset);
1135  else
1136  t += (time_t)(timeOffset);
1137 
1138  if (gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc)) {
1139  int Y = tm_gmt.tm_year;
1140  int M = tm_gmt.tm_mon + 1;
1141  int D = tm_gmt.tm_mday;
1142  int L = (M == 1 || M == 2) ? 1 : 0;
1143  int MJD = 14956 + D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001);
1144 #define DEC2BCD(d) (uint8_t((((d) / 10) << 4) + ((d) % 10)))
1145  struct tTime { unsigned short mjd; uint8_t h, m, s; short offset; };
1146  tTime T;
1147  T.mjd = htons(MJD);
1148  T.h = DEC2BCD(tm_gmt.tm_hour);
1149  T.m = DEC2BCD(tm_gmt.tm_min);
1150  T.s = DEC2BCD(tm_gmt.tm_sec);
1151  T.offset = static_cast<short>(htons(tm_loc.tm_gmtoff / 60));
1152 
1153  dbgprotocol("%d: ==> Date Time\n", SessionId());
1154  SendData(AOT_DATE_TIME, 7, (uint8_t*)&T);
1155  //XXX return value of all SendData() calls???
1156  return true;
1157  }
1158  return false;
1159 }
1160 
1161 bool cCiDateTime::Process(int Length, const uint8_t *Data)
1162 {
1163  if (Data) {
1164  int Tag = GetTag(Length, &Data);
1165  switch (Tag) {
1166  case AOT_DATE_TIME_ENQ: {
1167  interval = 0;
1168  int l = 0;
1169  const uint8_t *d = GetData(Data, l);
1170  if (l > 0)
1171  interval = *d;
1172  dbgprotocol("%d: <== Date Time Enq, interval = %d\n", SessionId(), interval);
1173  lastTime = time(nullptr);
1174  return SendDateTime();
1175  }
1176  break;
1177  default: esyslog("ERROR: CI date time: unknown tag %06X", Tag);
1178  return false;
1179  }
1180  }
1181  else if (interval && time(nullptr) - lastTime > interval) {
1182  lastTime = time(nullptr);
1183  return SendDateTime();
1184  }
1185  return true;
1186 }
1187 
1188 // --- cCiMMI ----------------------------------------------------------------
1189 
1190 // Close MMI Commands:
1191 
1192 #define CLOSE_MMI_IMMEDIATE 0x00
1193 #define CLOSE_MMI_DELAY 0x01
1194 
1195 // Display Control Commands:
1196 
1197 #define DCC_SET_MMI_MODE 0x01
1198 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
1199 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
1200 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
1201 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
1202 
1203 // MMI Modes:
1204 
1205 #define MM_HIGH_LEVEL 0x01
1206 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
1207 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
1208 
1209 // Display Reply IDs:
1210 
1211 #define DRI_MMI_MODE_ACK 0x01
1212 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
1213 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
1214 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
1215 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
1216 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
1217 #define DRI_UNKNOWN_MMI_MODE 0xF1
1218 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
1219 
1220 // Enquiry Flags:
1221 
1222 #define EF_BLIND 0x01
1223 
1224 // Answer IDs:
1225 
1226 #define AI_CANCEL 0x00
1227 #define AI_ANSWER 0x01
1228 
1229 class cCiMMI : public cCiSession {
1230 private:
1231  char *GetText(int &Length, const uint8_t **Data);
1234 public:
1236  virtual ~cCiMMI();
1237  bool Process(int Length = 0, const uint8_t *Data = nullptr) override; // cCiSession
1238  bool HasUserIO(void) override { return menu || enquiry; } // cCiSession
1239  cCiMenu *Menu(void);
1240  cCiEnquiry *Enquiry(void);
1241  bool SendMenuAnswer(uint8_t Selection);
1242  bool SendAnswer(const char *Text);
1243  };
1244 
1246 :cCiSession(SessionId, RI_MMI, Tc)
1247 {
1248  dbgprotocol("New MMI (session id %d)\n", SessionId);
1249  menu = nullptr;
1250  enquiry = nullptr;
1251 }
1252 
1254 {
1255  delete menu;
1256  delete enquiry;
1257 }
1258 
1259 char *cCiMMI::GetText(int &Length, const uint8_t **Data)
1267 {
1268  int Tag = GetTag(Length, Data);
1269  if (Tag == AOT_TEXT_LAST) {
1270  char *s = GetString(Length, Data);
1271  dbgprotocol("%d: <== Text Last '%s'\n", SessionId(), s);
1272  return s;
1273  }
1274  else
1275  esyslog("CI MMI: unexpected text tag: %06X", Tag);
1276  return nullptr;
1277 }
1278 
1279 bool cCiMMI::Process(int Length, const uint8_t *Data)
1280 {
1281  if (Data) {
1282  int Tag = GetTag(Length, &Data);
1283  switch (Tag) {
1284  case AOT_DISPLAY_CONTROL: {
1285  dbgprotocol("%d: <== Display Control\n", SessionId());
1286  int l = 0;
1287  const uint8_t *d = GetData(Data, l);
1288  if (l > 0) {
1289  switch (*d) {
1290  case DCC_SET_MMI_MODE:
1291  if (l == 2 && *++d == MM_HIGH_LEVEL) {
1292  struct tDisplayReply { uint8_t id; uint8_t mode; };
1293  tDisplayReply dr;
1294  dr.id = DRI_MMI_MODE_ACK;
1295  dr.mode = MM_HIGH_LEVEL;
1296  dbgprotocol("%d: ==> Display Reply\n", SessionId());
1297  SendData(AOT_DISPLAY_REPLY, 2, (uint8_t *)&dr);
1298  }
1299  break;
1300  default: esyslog("CI MMI: unsupported display control command %02X", *d);
1301  return false;
1302  }
1303  }
1304  }
1305  break;
1306  case AOT_LIST_LAST:
1307  case AOT_MENU_LAST: {
1308  dbgprotocol("%d: <== Menu Last\n", SessionId());
1309  delete menu;
1310  menu = new cCiMenu(this, Tag == AOT_MENU_LAST);
1311  int l = 0;
1312  const uint8_t *d = GetData(Data, l);
1313  if (l > 0) {
1314  // since the specification allows choiceNb to be undefined it is useless, so let's just skip it:
1315  d++;
1316  l--;
1317  if (l > 0) menu->titleText = GetText(l, &d);
1318  if (l > 0) menu->subTitleText = GetText(l, &d);
1319  if (l > 0) menu->bottomText = GetText(l, &d);
1320  while (l > 0) {
1321  char *s = GetText(l, &d);
1322  if (s) {
1323  if (!menu->AddEntry(s))
1324  free(s);
1325  }
1326  else
1327  break;
1328  }
1329  }
1330  }
1331  break;
1332  case AOT_ENQ: {
1333  dbgprotocol("%d: <== Enq\n", SessionId());
1334  delete enquiry;
1335  enquiry = new cCiEnquiry(this);
1336  int l = 0;
1337  const uint8_t *d = GetData(Data, l);
1338  if (l > 0) {
1339  uint8_t blind = *d++;
1340  //XXX GetByte()???
1341  l--;
1342  enquiry->blind = blind & EF_BLIND;
1343  enquiry->expectedLength = *d++;
1344  l--;
1345  // I really wonder why there is no text length field here...
1346  enquiry->text = CopyString(l, d);
1347  }
1348  }
1349  break;
1350  case AOT_CLOSE_MMI: {
1351  int l = 0;
1352  const uint8_t *d = GetData(Data, l);
1353 
1354  if(l > 0){
1355  switch(*d){
1356  case CLOSE_MMI_IMMEDIATE:
1357  dbgprotocol("%d <== Menu Close: immediate\n", SessionId());
1358  break;
1359  case CLOSE_MMI_DELAY:
1360  dbgprotocol("%d <== Menu Close: delay\n", SessionId());
1361  break;
1362  default: esyslog("ERROR: CI MMI: unknown close_mmi_cmd_id %02X", *d);
1363  return false;
1364  }
1365  }
1366 
1367  break;
1368  }
1369  default: esyslog("ERROR: CI MMI: unknown tag %06X", Tag);
1370  return false;
1371  }
1372  }
1373  return true;
1374 }
1375 
1377 {
1378  cCiMenu *m = menu;
1379  menu = nullptr;
1380  return m;
1381 }
1382 
1384 {
1385  cCiEnquiry *e = enquiry;
1386  enquiry = nullptr;
1387  return e;
1388 }
1389 
1390 bool cCiMMI::SendMenuAnswer(uint8_t Selection)
1391 {
1392  dbgprotocol("%d: ==> Menu Answ\n", SessionId());
1393  SendData(AOT_MENU_ANSW, 1, &Selection);
1394  //XXX return value of all SendData() calls???
1395  return true;
1396 }
1397 
1398 bool cCiMMI::SendAnswer(const char *Text)
1399 {
1400  dbgprotocol("%d: ==> Answ\n", SessionId());
1401  struct tAnswer { uint8_t id; char text[256]; };//XXX
1402  tAnswer answer;
1403  answer.id = Text ? AI_ANSWER : AI_CANCEL;
1404  if (Text) {
1405  strncpy(answer.text, Text, sizeof(answer.text) - 1);
1406  answer.text[255] = '\0';
1407  }
1408  SendData(AOT_ANSW, Text ? strlen(Text) + 1 : 1, (uint8_t *)&answer);
1409  //XXX return value of all SendData() calls???
1410  return true;
1411 }
1412 
1413 // --- cCiMenu ---------------------------------------------------------------
1414 
1415 cCiMenu::cCiMenu(cCiMMI *MMI, bool Selectable)
1416 {
1417  mmi = MMI;
1419  titleText = subTitleText = bottomText = nullptr;
1420  numEntries = 0;
1421  for (int i = 0; i < MAX_CIMENU_ENTRIES; i++)
1422  entries[i] = nullptr;
1423 }
1424 
1426 {
1427  free(titleText);
1428  free(subTitleText);
1429  free(bottomText);
1430  for (int i = 0; i < numEntries; i++)
1431  free(entries[i]);
1432 }
1433 
1434 bool cCiMenu::AddEntry(char *s)
1435 {
1437  entries[numEntries++] = s;
1438  return true;
1439  }
1440  return false;
1441 }
1442 
1443 bool cCiMenu::Select(int Index)
1444 {
1445  if (mmi && -1 <= Index && Index < numEntries)
1446  return mmi->SendMenuAnswer(Index + 1);
1447  return false;
1448 }
1449 
1451 {
1452  return Select(-1);
1453 }
1454 
1455 // --- cCiEnquiry ------------------------------------------------------------
1456 
1458 {
1459  mmi = MMI;
1460  text = nullptr;
1461  blind = false;;
1462  expectedLength = 0;;
1463 }
1464 
1466 {
1467  free(text);
1468 }
1469 
1470 bool cCiEnquiry::Reply(const char *s)
1471 {
1472  return mmi ? mmi->SendAnswer(s) : false;
1473 }
1474 
1476 {
1477  return Reply(nullptr);
1478 }
1479 
1480 // --- cCiCaPmt --------------------------------------------------------------
1481 
1482 // Ca Pmt Cmd Ids:
1483 
1484 #define CPCI_OK_DESCRAMBLING 0x01
1485 #define CPCI_OK_MMI 0x02
1486 #define CPCI_QUERY 0x03
1487 #define CPCI_NOT_SELECTED 0x04
1488 
1489 cCiCaPmt::cCiCaPmt(int ProgramNumber, uint8_t cplm)
1490 {
1491  length = 0;
1492  capmt[length++] = cplm; // ca_pmt_list_management
1493  capmt[length++] = (ProgramNumber >> 8) & 0xFF;
1494  capmt[length++] = ProgramNumber & 0xFF;
1495  capmt[length++] = 0x01; // version_number, current_next_indicator - apparently vn doesn't matter, but cni must be 1
1496 
1497  // program_info_length
1499  capmt[length++] = 0x00;
1500  capmt[length++] = 0x00;
1501 }
1502 
1504 {
1505  if (length + 5 > int(sizeof(capmt)))
1506  {
1507  esyslog("ERROR: buffer overflow in CA_PMT");
1508  return;
1509  }
1510 
1511  capmt[length++] = type & 0xFF;
1512  capmt[length++] = (pid >> 8) & 0xFF;
1513  capmt[length++] = pid & 0xFF;
1514 
1515  // ES_info_length
1517  capmt[length++] = 0x00;
1518  capmt[length++] = 0x00;
1519 }
1520 
1538 void cCiCaPmt::AddCaDescriptor(int ca_system_id, int ca_pid, int data_len,
1539  const uint8_t *data)
1540 {
1541  if (!infoLengthPos)
1542  {
1543  esyslog("ERROR: adding CA descriptor without program/stream!");
1544  return;
1545  }
1546 
1547  if (length + data_len + 7 > int(sizeof(capmt)))
1548  {
1549  esyslog("ERROR: buffer overflow in CA_PMT");
1550  return;
1551  }
1552 
1553  // We are either at start of program descriptors or stream descriptors.
1554  if (infoLengthPos + 2 == length)
1555  capmt[length++] = CPCI_OK_DESCRAMBLING; // ca_pmt_cmd_id
1556 
1557  capmt[length++] = 0x09; // CA descriptor tag
1558  capmt[length++] = 4 + data_len; // descriptor length
1559 
1560  capmt[length++] = (ca_system_id >> 8) & 0xFF;
1561  capmt[length++] = ca_system_id & 0xFF;
1562  capmt[length++] = (ca_pid >> 8) & 0xFF;
1563  capmt[length++] = ca_pid & 0xFF;
1564 
1565  if (data_len > 0)
1566  {
1567  memcpy(&capmt[length], data, data_len);
1568  length += data_len;
1569  }
1570 
1571  // update program_info_length/ES_info_length
1572  int l = length - infoLengthPos - 2;
1573  capmt[infoLengthPos] = (l >> 8) & 0xFF;
1574  capmt[infoLengthPos + 1] = l & 0xFF;
1575 }
1576 
1577 // -- cLlCiHandler -------------------------------------------------------------
1578 
1579 cLlCiHandler::cLlCiHandler(int Fd, int NumSlots)
1580 {
1581  numSlots = NumSlots;
1582  newCaSupport = false;
1583  hasUserIO = false;
1584  for (int i = 0; i < MAX_CI_SESSION; i++)
1585  sessions[i] = nullptr;
1586  tpl = new cCiTransportLayer(Fd, numSlots);
1587  tc = nullptr;
1588  fdCa = Fd;
1589  needCaPmt = false;
1590 }
1591 
1593 {
1594  cMutexLock MutexLock(&mutex);
1595  for (int i = 0; i < MAX_CI_SESSION; i++)
1596  if (sessions[i] != nullptr)
1597  delete sessions[i];
1598  delete tpl;
1599  close(fdCa);
1600 }
1601 
1603 {
1604  int fd_ca = open(FileName, O_RDWR);
1605  if (fd_ca >= 0)
1606  {
1607  ca_caps_t Caps;
1608  if (ioctl(fd_ca, CA_GET_CAP, &Caps) == 0)
1609  {
1610  int NumSlots = Caps.slot_num;
1611  if (NumSlots > 0)
1612  {
1613  if (Caps.slot_type & CA_CI_LINK)
1614  return new cLlCiHandler(fd_ca, NumSlots);
1615  else if (Caps.slot_type & CA_CI)
1616  return new cHlCiHandler(fd_ca, NumSlots);
1617  else
1618  isyslog("CAM doesn't support either high or low level CI,"
1619  " Caps.slot_type=%i", Caps.slot_type);
1620  }
1621  else
1622  esyslog("ERROR: no CAM slots found");
1623  }
1624  else
1625  LOG_ERROR_STR(FileName);
1626  close(fd_ca);
1627  }
1628  return nullptr;
1629 }
1630 
1631 int cLlCiHandler::ResourceIdToInt(const uint8_t *Data)
1632 {
1633  return (ntohl(*(int *)Data));
1634 }
1635 
1636 bool cLlCiHandler::Send(uint8_t Tag, int SessionId, int ResourceId, int Status)
1637 {
1638  uint8_t buffer[16];
1639  uint8_t *p = buffer;
1640  *p++ = Tag;
1641  *p++ = 0x00; // will contain length
1642  if (Status >= 0)
1643  *p++ = Status;
1644  if (ResourceId) {
1645  *(int *)p = htonl(ResourceId);
1646  p += 4;
1647  }
1648  *(short *)p = htons(SessionId);
1649  p += 2;
1650  buffer[1] = p - buffer - 2; // length
1651  return tc && tc->SendData(p - buffer, buffer) == OK;
1652 }
1653 
1655 {
1656  for (int i = 0; i < MAX_CI_SESSION; i++) {
1657  if (sessions[i] && sessions[i]->SessionId() == SessionId)
1658  return sessions[i];
1659  }
1660  return nullptr;
1661 }
1662 
1664 {
1665  for (int i = 0; i < MAX_CI_SESSION; i++) {
1666  if (sessions[i] && sessions[i]->Tc()->Slot() == Slot && sessions[i]->ResourceId() == ResourceId)
1667  return sessions[i];
1668  }
1669  return nullptr;
1670 }
1671 
1673 {
1674  if (!GetSessionByResourceId(ResourceId, tc->Slot())) {
1675  for (int i = 0; i < MAX_CI_SESSION; i++) {
1676  if (!sessions[i]) {
1677  switch (ResourceId) {
1678  case RI_RESOURCE_MANAGER: return sessions[i] = new cCiResourceManager(i + 1, tc);
1679  case RI_APPLICATION_INFORMATION: return sessions[i] = new cCiApplicationInformation(i + 1, tc);
1681  return sessions[i] = new cCiConditionalAccessSupport(i + 1, tc);
1682  case RI_HOST_CONTROL: break; //XXX
1683  case RI_DATE_TIME: return sessions[i] = new cCiDateTime(i + 1, tc);
1684  case RI_MMI: return sessions[i] = new cCiMMI(i + 1, tc);
1685  }
1686  }
1687  }
1688  }
1689  return nullptr;
1690 }
1691 
1692 bool cLlCiHandler::OpenSession(int Length, const uint8_t *Data)
1693 {
1694  if (Length == 6 && *(Data + 1) == 0x04) {
1695  int ResourceId = ResourceIdToInt(Data + 2);
1696  dbgprotocol("OpenSession %08X\n", ResourceId);
1697  switch (ResourceId) {
1698  case RI_RESOURCE_MANAGER:
1701  case RI_HOST_CONTROL:
1702  case RI_DATE_TIME:
1703  case RI_MMI:
1704  {
1705  cCiSession *Session = CreateSession(ResourceId);
1706  if (Session)
1707  {
1709  Session->ResourceId(), SS_OK);
1710  return true;
1711  }
1712  esyslog("ERROR: can't create session for resource identifier: %08X",
1713  ResourceId);
1714  break;
1715  }
1716  default: esyslog("ERROR: unknown resource identifier: %08X", ResourceId);
1717  }
1718  }
1719  return false;
1720 }
1721 
1722 bool cLlCiHandler::CloseSession(int SessionId)
1723 {
1724  dbgprotocol("CloseSession %08X\n", SessionId);
1725  cCiSession *Session = GetSessionBySessionId(SessionId);
1726  if (Session && sessions[SessionId - 1] == Session) {
1727  delete Session;
1728  sessions[SessionId - 1] = nullptr;
1729  Send(ST_CLOSE_SESSION_RESPONSE, SessionId, 0, SS_OK);
1730  return true;
1731  }
1732  else {
1733  esyslog("ERROR: unknown session id: %d", SessionId);
1735  }
1736  return false;
1737 }
1738 
1740 {
1741  int result = 0;
1742  for (int i = 0; i < MAX_CI_SESSION; i++) {
1743  if (sessions[i] && sessions[i]->Tc()->Slot() == Slot) {
1744  CloseSession(sessions[i]->SessionId());
1745  result++;
1746  }
1747  }
1748  return result;
1749 }
1750 
1752 {
1753  bool result = true;
1754  cMutexLock MutexLock(&mutex);
1755 
1756  for (int Slot = 0; Slot < numSlots; Slot++)
1757  {
1758  tc = tpl->Process(Slot);
1759  if (tc)
1760  {
1761  int Length;
1762  const uint8_t *Data = tc->Data(Length);
1763  if (Data && Length > 1)
1764  {
1765  switch (*Data)
1766  {
1767  case ST_SESSION_NUMBER:
1768  if (Length > 4)
1769  {
1770  int SessionId = ntohs(*(short *)&Data[2]);
1771  cCiSession *Session = GetSessionBySessionId(SessionId);
1772  if (Session)
1773  {
1774  Session->Process(Length - 4, Data + 4);
1775  if (Session->ResourceId() == RI_APPLICATION_INFORMATION)
1776  {
1777 #if 0
1778  esyslog("Test: %x",
1779  ((cCiApplicationInformation*)Session)->GetApplicationManufacturer());
1780 #endif
1781  }
1782  }
1783  else
1784  esyslog("ERROR: unknown session id: %d", SessionId);
1785  }
1786  break;
1787 
1789  OpenSession(Length, Data);
1790  break;
1791 
1793  if (Length == 4)
1794  CloseSession(ntohs(*(short *)&Data[2]));
1795  break;
1796 
1797  case ST_CREATE_SESSION_RESPONSE: //XXX fall through to default
1798  case ST_CLOSE_SESSION_RESPONSE: //XXX fall through to default
1799  default:
1800  esyslog("ERROR: unknown session tag: %02X", *Data);
1801  }
1802  }
1803  }
1804  else if (CloseAllSessions(Slot))
1805  {
1806  tpl->ResetSlot(Slot);
1807  result = false;
1808  }
1809  else if (tpl->ModuleReady(Slot))
1810  {
1811  dbgprotocol("Module ready in slot %d\n", Slot);
1812  tpl->NewConnection(Slot);
1813  }
1814  }
1815 
1816  bool UserIO = false;
1817  needCaPmt = false;
1818  for (int i = 0; i < MAX_CI_SESSION; i++)
1819  {
1820  if (sessions[i] && sessions[i]->Process())
1821  {
1822  UserIO |= sessions[i]->HasUserIO();
1823  if (sessions[i]->ResourceId() == RI_CONDITIONAL_ACCESS_SUPPORT)
1824  {
1826  needCaPmt |= cas->NeedCaPmt();
1827  }
1828  }
1829  }
1830  hasUserIO = UserIO;
1831 
1832  if (newCaSupport)
1833  newCaSupport = result = false; // triggers new SetCaPmt at caller!
1834  return result;
1835 }
1836 
1838 {
1839  cMutexLock MutexLock(&mutex);
1841  return api ? api->EnterMenu() : false;
1842 }
1843 
1845 {
1846  cMutexLock MutexLock(&mutex);
1847  for (int Slot = 0; Slot < numSlots; Slot++) {
1848  cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI, Slot);
1849  if (mmi)
1850  return mmi->Menu();
1851  }
1852  return nullptr;
1853 }
1854 
1856 {
1857  cMutexLock MutexLock(&mutex);
1858  for (int Slot = 0; Slot < numSlots; Slot++) {
1859  cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI, Slot);
1860  if (mmi)
1861  return mmi->Enquiry();
1862  }
1863  return nullptr;
1864 }
1865 
1866 const unsigned short *cLlCiHandler::GetCaSystemIds(int Slot)
1867  {
1868  cMutexLock MutexLock(&mutex);
1870  return cas ? cas->GetCaSystemIds() : nullptr;
1871 }
1872 
1873 bool cLlCiHandler::SetCaPmt(cCiCaPmt &CaPmt, int Slot)
1874 {
1875  cMutexLock MutexLock(&mutex);
1877  return cas && cas->SendPMT(CaPmt);
1878 }
1879 
1880 void cLlCiHandler::SetTimeOffset(double offset_in_seconds)
1881 {
1882  cMutexLock MutexLock(&mutex);
1883  cCiDateTime *dt = nullptr;
1884 
1885  for (uint i = 0; i < (uint) NumSlots(); i++)
1886  {
1888  if (dt)
1889  dt->SetTimeOffset(offset_in_seconds);
1890  }
1891 }
1892 
1893 bool cLlCiHandler::Reset(int Slot)
1894 {
1895  cMutexLock MutexLock(&mutex);
1896  CloseAllSessions(Slot);
1897  return tpl->ResetSlot(Slot);
1898 }
1899 
1901 {
1902  return _connected;
1903 }
1904 
1905 // -- cHlCiHandler -------------------------------------------------------------
1906 
1907 cHlCiHandler::cHlCiHandler(int Fd, int NumSlots)
1908 {
1909  numSlots = NumSlots;
1910  numCaSystemIds = 0;
1911  caSystemIds[0] = 0;
1912  fdCa = Fd;
1913  state = 0;
1914  esyslog("New High level CI handler");
1915 }
1916 
1918 {
1919  cMutexLock MutexLock(&mutex);
1920  close(fdCa);
1921 }
1922 
1923 int cHlCiHandler::CommHL(unsigned tag, unsigned function, struct ca_msg *msg)
1924 {
1925  if (tag) {
1926  msg->msg[2] = tag & 0xff;
1927  msg->msg[1] = (tag & 0xff00) >> 8;
1928  msg->msg[0] = (tag & 0xff0000) >> 16;
1929  esyslog("Sending message=[%02x %02x %02x ]",
1930  msg->msg[0], msg->msg[1], msg->msg[2]);
1931  }
1932 
1933  return ioctl(fdCa, function, msg);
1934 }
1935 
1936 int cHlCiHandler::GetData(unsigned tag, struct ca_msg *msg)
1937 {
1938  return CommHL(tag, CA_GET_MSG, msg);
1939 }
1940 
1941 int cHlCiHandler::SendData(unsigned tag, struct ca_msg *msg)
1942 {
1943  return CommHL(tag, CA_SEND_MSG, msg);
1944 }
1945 
1947 {
1948  cMutexLock MutexLock(&mutex);
1949 
1950  struct ca_msg msg;
1951  switch(state) {
1952  case 0:
1953  // Get CA_system_ids
1954  /* Enquire */
1955  if ((SendData(AOT_CA_INFO_ENQ, &msg)) < 0) {
1956  esyslog("HLCI communication failed");
1957  } else {
1958  dbgprotocol("==> Ca Info Enquiry");
1959  /* Receive */
1960  if ((GetData(AOT_CA_INFO, &msg)) < 0) {
1961  esyslog("HLCI communication failed");
1962  } else {
1963  QString message("Debug: ");
1964  for(int i = 0; i < 20; i++) {
1965  message += QString("%1 ").arg(msg.msg[i]);
1966  }
1967  LOG(VB_GENERAL, LOG_DEBUG, message);
1968  dbgprotocol("<== Ca Info");
1969  int l = msg.msg[3];
1970  const uint8_t *d = &msg.msg[4];
1971  while (l > 1) {
1972  unsigned short id = ((unsigned short)(*d) << 8) | *(d + 1);
1973  dbgprotocol(" %04X", id);
1974  d += 2;
1975  l -= 2;
1977  caSystemIds[numCaSystemIds++] = id;
1979  }
1980  else
1981  esyslog("ERROR: too many CA system IDs!");
1982  }
1983  dbgprotocol("\n");
1984  }
1985  state = 1;
1986  break;
1987  }
1988  }
1989 
1990  bool result = true;
1991 
1992  return result;
1993 }
1994 
1996 {
1997  return false;
1998 }
1999 
2001 {
2002  return nullptr;
2003 }
2004 
2006 {
2007  return nullptr;
2008 }
2009 
2010 const unsigned short *cHlCiHandler::GetCaSystemIds(int)
2011 {
2012  return caSystemIds;
2013 }
2014 
2016 {
2017  cMutexLock MutexLock(&mutex);
2018  struct ca_msg msg;
2019 
2020  esyslog("Setting CA PMT.");
2021  state = 2;
2022 
2023  msg.msg[3] = CaPmt.length;
2024 
2025  if (CaPmt.length > (256 - 4))
2026  {
2027  esyslog("CA message too long");
2028  return false;
2029  }
2030 
2031  memcpy(&msg.msg[4], CaPmt.capmt, CaPmt.length);
2032 
2033  if ((SendData(AOT_CA_PMT, &msg)) < 0) {
2034  esyslog("HLCI communication failed");
2035  return false;
2036  }
2037 
2038  return true;
2039 }
2040 
2042 {
2043  if ((ioctl(fdCa, CA_RESET)) < 0) {
2044  esyslog("ioctl CA_RESET failed.");
2045  return false;
2046  }
2047  return true;
2048 }
2049 
2051 {
2052  if(state == 1)
2053  return true;
2054 
2055  return false;
2056 }
uint16_t applicationManufacturer
Definition: dvbci.cpp:939
#define T_TC_ERROR
Definition: dvbci.cpp:255
uint8_t capmt[2048]
XXX is there a specified maximum?
Definition: dvbci.h:129
bool SendDateTime(void)
Definition: dvbci.cpp:1126
cCiSession(int SessionId, int ResourceId, cCiTransportConnection *Tc)
Definition: dvbci.cpp:790
def write(text, progress=True)
Definition: mythburn.py:279
#define T_DELETE_TC
Definition: dvbci.cpp:251
void Lock(void)
Definition: dvbci.cpp:192
bool Send(uint8_t Tag, int SessionId, int ResourceId=0, int Status=-1)
Definition: dvbci.cpp:1636
cCiSession * CreateSession(int ResourceId)
Definition: dvbci.cpp:1672
static __inline struct tm * gmtime_r(const time_t *timep, struct tm *result)
Definition: compat.h:262
#define T_DATA_MORE
Definition: dvbci.cpp:257
#define ST_OPEN_SESSION_REQUEST
Definition: dvbci.cpp:699
#define T_NEW_TC
Definition: dvbci.cpp:254
int SessionId(void)
Definition: dvbci.cpp:784
#define T_RCV
Definition: dvbci.cpp:248
cMutex mutex
Definition: dvbci.h:203
uint16_t GetApplicationManufacturer()
Definition: dvbci.cpp:948
bool Process(int Length=0, const uint8_t *Data=nullptr) override
Definition: dvbci.cpp:1279
virtual int NumSlots(void)=0
#define OK
Definition: dvbci.cpp:69
const unsigned short * GetCaSystemIds(void)
Definition: dvbci.cpp:1026
bool Process(void) override
Definition: dvbci.cpp:1751
static cCiHandler * CreateCiHandler(const char *FileName)
Definition: dvbci.cpp:1602
#define AOT_CLOSE_MMI
Definition: dvbci.cpp:739
#define ST_CREATE_SESSION_RESPONSE
Definition: dvbci.cpp:702
int resourceId
Definition: dvbci.cpp:774
#define RI_HOST_CONTROL
Definition: dvbci.cpp:716
#define T_REQUEST_TC
Definition: dvbci.cpp:253
#define MM_HIGH_LEVEL
Definition: dvbci.cpp:1205
#define RI_DATE_TIME
Definition: dvbci.cpp:717
const uint8_t * Data(int &Length)
Definition: dvbci.cpp:534
char * GetApplicationString()
Definition: dvbci.cpp:947
int fdCa
Definition: dvbci.h:204
cCiTransportConnection * tc
Definition: dvbci.h:170
int GetData(unsigned tag, struct ca_msg *msg)
Definition: dvbci.cpp:1936
virtual bool Process(int Length=0, const uint8_t *Data=nullptr)
Definition: dvbci.cpp:860
int SendData(unsigned tag, struct ca_msg *msg)
Definition: dvbci.cpp:1941
cCiMenu * menu
Definition: dvbci.cpp:1232
cCiTransportConnection * Process(int Slot)
Definition: dvbci.cpp:655
cTPDU(void)
Definition: dvbci.cpp:265
bool OpenSession(int Length, const uint8_t *Data)
Definition: dvbci.cpp:1692
cCiDateTime(int SessionId, cCiTransportConnection *Tc)
Definition: dvbci.cpp:1111
int Slot(void) const
Definition: dvbci.cpp:412
#define WRKRND_TIME_BEFORE_ENTER_MENU
Definition: dvbci.cpp:77
bool HasUserIO(void) override
Definition: dvbci.cpp:1238
#define RI_MMI
Definition: dvbci.cpp:718
int SendTPDU(uint8_t Tag, int Length=0, const uint8_t *Data=nullptr)
Definition: dvbci.cpp:445
bool Process(int Length=0, const uint8_t *Data=nullptr) override
Definition: dvbci.cpp:884
int ResourceId(void)
Definition: dvbci.cpp:785
cCiMMI(int SessionId, cCiTransportConnection *Tc)
Definition: dvbci.cpp:1245
bool Lock(cMutex *Mutex)
Definition: dvbci.cpp:227
uint8_t data[MAX_TPDU_SIZE]
Definition: dvbci.cpp:262
char * bottomText
Definition: dvbci.h:80
int timeOffset
Definition: dvbci.cpp:1103
#define MAXCASYSTEMIDS
Definition: dvbci.h:44
bool SendPMT(cCiCaPmt &CaPmt)
Definition: dvbci.cpp:1087
int numSlots
Definition: dvbci.h:164
cCiSession * GetSessionBySessionId(int SessionId)
Definition: dvbci.cpp:1654
static __inline struct tm * localtime_r(const time_t *timep, struct tm *result)
Definition: compat.h:279
#define MAX_CI_CONNECT
Definition: dvbci.cpp:594
#define AOT_LIST_LAST
Definition: dvbci.cpp:751
const cCiTransportConnection * Tc(void)
Definition: dvbci.cpp:783
#define AOT_DATE_TIME_ENQ
Definition: dvbci.cpp:737
bool Reset(int Slot)
Definition: dvbci.cpp:1893
unsigned int uint
Definition: compat.h:140
int GetTag(int &Length, const uint8_t **Data)
Definition: dvbci.cpp:801
static bool DebugProtocol
Definition: dvbci.cpp:64
bool newCaSupport
Definition: dvbci.h:165
#define AOT_APPLICATION_INFO
Definition: dvbci.cpp:727
cCiMMI * mmi
Definition: dvbci.h:100
int CommHL(unsigned tag, unsigned function, struct ca_msg *msg)
Definition: dvbci.cpp:1923
bool Selectable(void)
Definition: dvbci.h:92
char * GetText(int &Length, const uint8_t **Data)
Definition: dvbci.cpp:1259
#define CAM_READ_TIMEOUT
Definition: dvbci.cpp:451
#define ST_OPEN_SESSION_RESPONSE
Definition: dvbci.cpp:700
#define T_SB
Definition: dvbci.cpp:247
bool EnterMenu(int Slot) override
Definition: dvbci.cpp:1837
cCiTransportConnection(void)
Definition: dvbci.cpp:419
bool Cancel(void)
Definition: dvbci.cpp:1450
const uint8_t * GetData(const uint8_t *Data, int &Length)
Definition: dvbci.cpp:365
#define AOT_CA_PMT
Definition: dvbci.cpp:731
virtual bool HasUserIO(void)
Definition: dvbci.cpp:786
static char * GetString(int &Length, const uint8_t **Data)
Definition: dvbci.cpp:156
#define AOT_ENQ
Definition: dvbci.cpp:746
int SendData(int Tag, int Length=0, const uint8_t *Data=nullptr)
Definition: dvbci.cpp:826
static ssize_t safe_read(int filedes, void *buffer, size_t size)
Definition: dvbci.cpp:83
int numEntries
Definition: dvbci.h:82
bool NeedCaPmt(void) override
Definition: dvbci.cpp:2050
State
Definition: zmserver.h:62
cLlCiHandler(int Fd, int NumSlots)
Definition: dvbci.cpp:1579
#define LOG_ERROR_STR(s)
Definition: dvbci.cpp:59
#define RI_APPLICATION_INFORMATION
Definition: dvbci.cpp:714
#define MAX_CI_SESSION
Definition: dvbci.h:137
int CreateConnection(void)
Definition: dvbci.cpp:541
int Write(int fd)
Definition: dvbci.cpp:323
def read(device=None, features=[])
Definition: disc.py:35
#define AI_CANCEL
Definition: dvbci.cpp:1226
unsigned char b
Definition: ParseText.cpp:340
unsigned short caSystemIds[MAXCASYSTEMIDS+1]
Definition: dvbci.h:208
void AddCaDescriptor(int ca_system_id, int ca_pid, int data_len, const uint8_t *data)
Definition: dvbci.cpp:1538
int sessionId
Definition: dvbci.cpp:773
int infoLengthPos
Definition: dvbci.h:128
#define MAX_DUMP
const unsigned short * GetCaSystemIds(int Slot) override
Definition: dvbci.cpp:1866
#define CLOSE_MMI_DELAY
Definition: dvbci.cpp:1193
bool blind
Definition: dvbci.h:102
cMutex mutex
Definition: dvbci.h:162
int numSlots
Definition: dvbci.h:205
~cMutex()
Definition: dvbci.cpp:187
char * titleText
Definition: dvbci.h:78
bool needCaPmt
Definition: dvbci.h:167
int numCaSystemIds
Definition: dvbci.h:207
cCiCaPmt(int ProgramNumber, uint8_t cplm=CPLM_ONLY)
Definition: dvbci.cpp:1489
static bool _connected
Definition: dvbci.cpp:65
#define AOT_ENTER_MENU
Definition: dvbci.cpp:728
cCiTransportConnection * tc
Definition: dvbci.cpp:775
pthread_mutex_t mutex
Definition: dvbci.h:49
#define POLL_INTERVAL
Definition: dvbci.cpp:567
eState State(void)
Definition: dvbci.cpp:406
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
bool connected() const
Definition: dvbci.cpp:1900
~cCiMenu()
Definition: dvbci.cpp:1425
struct timeval last_poll
Definition: dvbci.cpp:398
#define MAX_TPDU_DATA
Definition: dvbci.cpp:243
#define close
Definition: compat.h:16
uint8_t Tag(void)
Definition: dvbci.cpp:269
char * subTitleText
Definition: dvbci.h:79
#define AOT_PROFILE
Definition: dvbci.cpp:724
cCiEnquiry(cCiMMI *MMI)
Definition: dvbci.cpp:1457
#define AOT_ANSW
Definition: dvbci.cpp:747
bool locked
Definition: dvbci.h:62
#define AI_ANSWER
Definition: dvbci.cpp:1227
#define AOT_APPLICATION_INFO_ENQ
Definition: dvbci.cpp:726
bool Cancel(void)
Definition: dvbci.cpp:1475
cCiEnquiry * Enquiry(void)
Definition: dvbci.cpp:1383
cCiSession * GetSessionByResourceId(int ResourceId, int Slot)
Definition: dvbci.cpp:1663
cCiEnquiry * GetEnquiry(void) override
Definition: dvbci.cpp:2005
int RecvTPDU(void)
Definition: dvbci.cpp:453
cCiEnquiry * enquiry
Definition: dvbci.cpp:1233
#define CPCI_OK_DESCRAMBLING
Definition: dvbci.cpp:1484
static const uint16_t * d
bool SendAnswer(const char *Text)
Definition: dvbci.cpp:1398
int RecvData(void)
Definition: dvbci.cpp:527
#define T_CREATE_TC
Definition: dvbci.cpp:249
int expectedLength
Definition: dvbci.h:103
unsigned char t
Definition: ParseText.cpp:340
#define AOT_DISPLAY_REPLY
Definition: dvbci.cpp:741
bool Process(void) override
Definition: dvbci.cpp:1946
cCiMenu * GetMenu(void) override
Definition: dvbci.cpp:1844
uint8_t Tcid(void)
Definition: dvbci.cpp:268
bool ResetSlot(int Slot)
Definition: dvbci.cpp:631
#define dsyslog(a...)
Definition: dvbci.cpp:56
int size
Definition: dvbci.cpp:261
#define esyslog(a...)
Definition: dvbci.cpp:54
uint8_t Slot(void)
Definition: dvbci.cpp:267
bool Reply(const char *s)
Definition: dvbci.cpp:1470
#define ST_CLOSE_SESSION_REQUEST
Definition: dvbci.cpp:703
#define RI_RESOURCE_MANAGER
Definition: dvbci.cpp:713
void Dump(bool Outgoing)
Definition: dvbci.cpp:344
unsigned short uint16_t
Definition: iso6937tables.h:1
cCiTransportConnection tc[MAX_CI_CONNECT]
Definition: dvbci.cpp:600
#define AOT_DISPLAY_CONTROL
Definition: dvbci.cpp:740
#define RI_CONDITIONAL_ACCESS_SUPPORT
Definition: dvbci.cpp:715
static uint8_t * SetLength(uint8_t *Data, int Length)
Definition: dvbci.cpp:117
bool Process(int Length=0, const uint8_t *Data=nullptr) override
Definition: dvbci.cpp:1161
bool SetCaPmt(cCiCaPmt &CaPmt)
cCiConditionalAccessSupport(int SessionId, cCiTransportConnection *Tc)
Definition: dvbci.cpp:1031
Definition: dvbci.h:46
Definition: dvbci.cpp:259
#define T_CTC_REPLY
Definition: dvbci.cpp:250
uint8_t Status(void)
Definition: dvbci.cpp:377
bool DataAvailable(void)
Definition: dvbci.cpp:408
cCiApplicationInformation(int SessionId, cCiTransportConnection *Tc)
Definition: dvbci.cpp:952
#define T_DTC_REPLY
Definition: dvbci.cpp:252
#define DATA_INDICATOR
Definition: dvbci.cpp:245
int CloseAllSessions(int Slot)
Definition: dvbci.cpp:1739
cCiTransportLayer(int Fd, int NumSlots)
Definition: dvbci.cpp:609
bool Select(int Index)
Definition: dvbci.cpp:1443
cMutex * mutex
Definition: dvbci.h:61
virtual ~cLlCiHandler()
Definition: dvbci.cpp:1592
time_t lastTime
Definition: dvbci.cpp:1102
int LastResponse(void)
Definition: dvbci.cpp:407
void Init(int Fd, uint8_t Slot, uint8_t Tcid)
Definition: dvbci.cpp:432
cCiEnquiry * GetEnquiry(void) override
Definition: dvbci.cpp:1855
bool Process(int Length=0, const uint8_t *Data=nullptr) override
Definition: dvbci.cpp:969
static char * CopyString(int Length, const uint8_t *Data)
Definition: dvbci.cpp:144
#define CLOSE_MMI_IMMEDIATE
Definition: dvbci.cpp:1192
#define ST_SESSION_NUMBER
Definition: dvbci.cpp:698
int NumSlots(void) override
Definition: dvbci.h:182
cCiResourceManager(int SessionId, cCiTransportConnection *Tc)
Definition: dvbci.cpp:877
bool CloseSession(int SessionId)
Definition: dvbci.cpp:1722
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void SetTimeOffset(double offset)
Definition: dvbci.cpp:1120
~cMutexLock()
Definition: dvbci.cpp:221
virtual ~cCiMMI()
Definition: dvbci.cpp:1253
~cCiEnquiry()
Definition: dvbci.cpp:1465
cHlCiHandler(int Fd, int NumSlots)
Definition: dvbci.cpp:1907
#define ST_CLOSE_SESSION_RESPONSE
Definition: dvbci.cpp:704
int state
Definition: dvbci.h:206
Definition: dvbci.h:72
#define ERROR
Definition: dvbci.cpp:71
int SendData(int Length, const uint8_t *Data)
Definition: dvbci.cpp:510
uint16_t GetManufacturerCode()
Definition: dvbci.cpp:949
#define TIMEOUT
Definition: dvbci.cpp:70
#define SIZE_INDICATOR
Definition: dvbci.cpp:81
char * entries[MAX_CIMENU_ENTRIES]
Definition: dvbci.h:81
int fdCa
Definition: dvbci.h:163
bool SendMenuAnswer(uint8_t Selection)
Definition: dvbci.cpp:1390
#define AOT_CA_INFO_ENQ
Definition: dvbci.cpp:729
int locked
Definition: dvbci.h:51
static bool DumpTPDUDataTransfer
Definition: dvbci.cpp:63
#define D(i, j)
#define dbgprotocol(a...)
Definition: dvbci.cpp:67
cCiMenu * Menu(void)
Definition: dvbci.cpp:1376
#define AOT_DATE_TIME
Definition: dvbci.cpp:738
bool ModuleReady(int Slot)
Definition: dvbci.cpp:644
void AddElementaryStream(int type, int pid)
Definition: dvbci.cpp:1503
#define MALLOC(type, size)
Definition: dvbci.cpp:51
static const uint8_t * GetLength(const uint8_t *Data, int &Length)
Definition: dvbci.cpp:95
#define AOT_PROFILE_ENQ
Definition: dvbci.cpp:723
const unsigned short * GetCaSystemIds(int Slot) override
Definition: dvbci.cpp:2010
cCiTransportLayer * tpl
Definition: dvbci.h:169
virtual ~cCiSession()
Definition: dvbci.cpp:797
const uint8_t * GetData(const uint8_t *Data, int &Length)
Definition: dvbci.cpp:820
#define AOT_MENU_ANSW
Definition: dvbci.cpp:750
bool hasUserIO
Definition: dvbci.h:166
unsigned short caSystemIds[MAXCASYSTEMIDS+1]
Definition: dvbci.cpp:1021
#define T_DATA_LAST
Definition: dvbci.cpp:256
void SetTimeOffset(double offset_in_seconds) override
Definition: dvbci.cpp:1880
#define DCC_SET_MMI_MODE
Definition: dvbci.cpp:1197
bool Reset(int Slot)
Definition: dvbci.cpp:2041
cCiMenu(cCiMMI *MMI, bool Selectable)
Definition: dvbci.cpp:1415
#define DEC2BCD(d)
int NumSlots(void) override
Definition: dvbci.h:215
virtual ~cHlCiHandler()
Definition: dvbci.cpp:1917
cCiMenu * GetMenu(void) override
Definition: dvbci.cpp:2000
cCiMMI * mmi
Definition: dvbci.h:76
pid_t lockingPid
Definition: dvbci.h:50
bool SetCaPmt(cCiCaPmt &CaPmt)
eState
Definition: dvbci.cpp:388
int Read(int fd)
Definition: dvbci.cpp:332
#define isyslog(a...)
Definition: dvbci.cpp:55
char * text
Definition: dvbci.h:101
#define AOT_TEXT_LAST
Definition: dvbci.cpp:742
cCiSession * sessions[MAX_CI_SESSION]
Definition: dvbci.h:168
#define SS_NOT_ALLOCATED
Definition: dvbci.cpp:709
#define SS_OK
Definition: dvbci.cpp:708
cMutexLock(cMutex *Mutex=nullptr)
Definition: dvbci.cpp:214
#define AOT_CA_INFO
Definition: dvbci.cpp:730
#define AOT_MENU_LAST
Definition: dvbci.cpp:748
const uint8_t * Data(int &Length)
Definition: dvbci.cpp:270
bool EnterMenu(int Slot) override
Definition: dvbci.cpp:1995
bool Process(int Length=0, const uint8_t *Data=nullptr) override
Definition: dvbci.cpp:1040
#define MAX_CONNECT_RETRIES
Definition: dvbci.cpp:539
int length
Definition: dvbci.h:127
#define MAX_TPDU_SIZE
Definition: dvbci.cpp:242
int interval
Definition: dvbci.cpp:1101
virtual ~cCiApplicationInformation()
Definition: dvbci.cpp:964
#define DRI_MMI_MODE_ACK
Definition: dvbci.cpp:1211
int ResourceIdToInt(const uint8_t *Data)
Definition: dvbci.cpp:1631
#define AOT_NONE
Definition: dvbci.cpp:722
bool selectable
Definition: dvbci.h:77
bool AddEntry(char *s)
Definition: dvbci.cpp:1434
#define AOT_PROFILE_CHANGE
Definition: dvbci.cpp:725
#define EF_BLIND
Definition: dvbci.cpp:1222
cMutex(void)
Definition: dvbci.cpp:180
void Unlock(void)
Definition: dvbci.cpp:201
cCiTransportConnection * NewConnection(int Slot)
Definition: dvbci.cpp:617