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