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