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