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