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