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 <array>
30#include <cctype>
31#include <cerrno>
32#include <cstring>
33#include <ctime>
34#include <fcntl.h>
35#include <linux/dvb/ca.h>
36#include <netinet/in.h>
37#include <poll.h>
38#include <sys/ioctl.h>
39#include <sys/time.h>
40#include <unistd.h>
41#ifdef __FreeBSD__
42# include <stdlib.h>
43#else
44# include <malloc.h>
45#endif
46
47#include <QString>
48
50
51// NOLINTBEGIN(cppcoreguidelines-macro-usage)
52#define esyslog(a...) LOG(VB_GENERAL, LOG_ERR, QString::asprintf(a))
53#define isyslog(a...) LOG(VB_DVBCAM, LOG_INFO, QString::asprintf(a))
54#define dsyslog(a...) LOG(VB_DVBCAM, LOG_DEBUG, QString::asprintf(a))
55
56#define LOG_ERROR esyslog("ERROR (%s,%d): %m", __FILE__, __LINE__)
57#define LOG_ERROR_STR(s) esyslog("ERROR: %s: %m", s)
58// NOLINTEND(cppcoreguidelines-macro-usage)
59
60
61// Set these to 'true' for debug output:
62static bool sDumpTPDUDataTransfer = false;
63static bool sDebugProtocol = false;
64static bool sConnected = false;
65
66// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
67#define dbgprotocol(a...) if (sDebugProtocol) LOG(VB_DVBCAM, LOG_DEBUG, QString::asprintf(a))
68
69static constexpr int OK { 0 };
70static constexpr int TIMEOUT { -1 };
71static constexpr int ERROR { -2 };
72
73// --- Workarounds -----------------------------------------------------------
74
75// The Irdeto AllCAM 4.7 (and maybe others, too) does not react on AOT_ENTER_MENU
76// during the first few seconds of a newly established connection
77static constexpr time_t WRKRND_TIME_BEFORE_ENTER_MENU { 15 }; // seconds
78
79// --- Helper functions ------------------------------------------------------
80
81static constexpr int SIZE_INDICATOR { 0x80 };
82
83static ssize_t safe_read(int filedes, void *buffer, size_t size)
84{
85 for (;;) {
86 ssize_t p = read(filedes, buffer, size);
87 if (p < 0 && (errno == EINTR || errno == EAGAIN)) {
88 dsyslog("EINTR while reading from file handle %d - retrying", filedes);
89 continue;
90 }
91 return p;
92 }
93}
94
95static const uint8_t *GetLength(const uint8_t *Data, int &Length)
106{
107 Length = *Data++;
108 if ((Length & SIZE_INDICATOR) != 0) {
109 int l = Length & ~SIZE_INDICATOR;
110 Length = 0;
111 for (int i = 0; i < l; i++)
112 Length = (Length << 8) | *Data++;
113 }
114 return Data;
115}
116
117static uint8_t *SetLength(uint8_t *Data, int Length)
127{
128 uint8_t *p = Data;
129 if (Length < 128)
130 *p++ = Length;
131 else {
132 int n = sizeof(Length);
133 for (int i = n - 1; i >= 0; i--) {
134 int b = (Length >> (8 * i)) & 0xFF;
135 if (p != Data || b)
136 *++p = b;
137 }
138 *Data = (p - Data) | SIZE_INDICATOR;
139 p++;
140 }
141 return p;
142}
143
145static void SetLength(std::vector<uint8_t> &Data, int Length)
146{
147 if (Length < 128)
148 {
149 Data.push_back(Length);
150 return;
151 }
152
153 // This will be replaced with the number of bytes in the length
154 size_t len_offset = Data.size();
155 Data.push_back(0);
156
157 int n = sizeof(Length);
158 for (int i = n - 1; i >= 0; i--) {
159 int b = (Length >> (8 * i)) & 0xFF;
160 if ((len_offset != Data.size()) || b)
161 Data.push_back(b);
162 }
163 Data[len_offset] = (Data.size() - len_offset) | SIZE_INDICATOR;
164}
165
166static char *CopyString(int Length, const uint8_t *Data)
171{
172 char *s = (char *)malloc(Length + 1);
173 strncpy(s, (char *)Data, Length);
174 s[Length] = 0;
175 return s;
176}
177
178static char *GetString(int &Length, const uint8_t **Data)
186{
187 if (Length > 0 && Data && *Data) {
188 int l = 0;
189 const uint8_t *d = GetLength(*Data, l);
190 char *s = CopyString(l, d);
191 Length -= d - *Data + l;
192 *Data = d + l;
193 return s;
194 }
195 return nullptr;
196}
197
198
199
200// --- cMutex ----------------------------------------------------------------
201
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:
951public:
954 bool Process(int Length = 0, const uint8_t *Data = nullptr) override; // cCiSession
955 bool EnterMenu(void);
956 char *GetApplicationString() { return strdup(m_menuString); };
959 };
960
963{
964 dbgprotocol("New Application Information (session id %d)\n", SessionId);
965 m_state = 0;
966 m_creationTime = time(nullptr);
970 m_menuString = nullptr;
971}
972
974{
975 free(m_menuString);
976}
977
978bool cCiApplicationInformation::Process(int Length, const uint8_t *Data)
979{
980 if (Data) {
981 int Tag = GetTag(Length, &Data);
982 switch (Tag) {
984 dbgprotocol("%d: <== Application Info\n", SessionId());
985 int l = 0;
986 const uint8_t *d = GetData(Data, l);
987 l -= 1;
988 if (l < 0) break;
989 m_applicationType = *d++;
990 l -= 2;
991 if (l < 0) break;
993 d += 2;
994 l -= 2;
995 if (l < 0) break;
996 m_manufacturerCode = ntohs(*(uint16_t *)d);
997 d += 2;
998 free(m_menuString);
999 m_menuString = GetString(l, &d);
1000 isyslog("CAM: %s, %02X, %04X, %04X", m_menuString, m_applicationType,
1002 }
1003 m_state = 2;
1004 break;
1005 default: esyslog("ERROR: CI application information: unknown tag %06X", Tag);
1006 return false;
1007 }
1008 }
1009 else if (m_state == 0) {
1010 dbgprotocol("%d: ==> Application Info Enq\n", SessionId());
1012 m_state = 1;
1013 }
1014 return true;
1015}
1016
1018{
1019 if (m_state == 2 && time(nullptr) - m_creationTime > WRKRND_TIME_BEFORE_ENTER_MENU) {
1020 dbgprotocol("%d: ==> Enter Menu\n", SessionId());
1022 return true;//XXX
1023 }
1024 return false;
1025}
1026
1027// --- cCiConditionalAccessSupport -------------------------------------------
1028
1030private:
1031 int m_state {0};
1033 bool m_needCaPmt {false};
1034public:
1036 bool Process(int Length = 0, const uint8_t *Data = nullptr) override; // cCiSession
1038 bool SendPMT(const cCiCaPmt &CaPmt);
1039 bool NeedCaPmt(void) const { return m_needCaPmt; }
1040 };
1041
1043 int SessionId, cCiTransportConnection *Tc) :
1045{
1046 dbgprotocol("New Conditional Access Support (session id %d)\n", SessionId);
1047}
1048
1049bool cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
1050{
1051 if (Data) {
1052 int Tag = GetTag(Length, &Data);
1053 switch (Tag) {
1054 case AOT_CA_INFO: {
1055 dbgprotocol("%d: <== Ca Info", SessionId());
1056 int l = 0;
1057 const uint8_t *d = GetData(Data, l);
1058 while (l > 1) {
1059 unsigned short id = ((unsigned short)(*d) << 8) | *(d + 1);
1060 dbgprotocol(" %04X", id);
1061 d += 2;
1062 l -= 2;
1063
1064 // Make sure the id is not already present
1065 if (std::find(m_caSystemIds.cbegin(), m_caSystemIds.cend(), id)
1066 != m_caSystemIds.end())
1067 continue;
1068
1069 // Insert before the last element.
1070 m_caSystemIds.emplace_back(id);
1071 }
1072
1073 dbgprotocol("\n");
1074 }
1075 m_state = 2;
1076 m_needCaPmt = true;
1077 break;
1078 default: esyslog("ERROR: CI conditional access support: unknown tag %06X", Tag);
1079 return false;
1080 }
1081 }
1082 else if (m_state == 0) {
1083 dbgprotocol("%d: ==> Ca Info Enq\n", SessionId());
1085 m_state = 1;
1086 }
1087 return true;
1088}
1089
1091{
1092 if (m_state == 2) {
1093 SendData(AOT_CA_PMT, CaPmt.m_length, CaPmt.m_capmt);
1094 m_needCaPmt = false;
1095 return true;
1096 }
1097 return false;
1098}
1099
1100// --- cCiDateTime -----------------------------------------------------------
1101
1102class cCiDateTime : public cCiSession {
1103private:
1104 int m_interval { 0 };
1105 time_t m_lastTime { 0 };
1106 int m_timeOffset { 0 };
1107 bool SendDateTime(void);
1108public:
1110 bool Process(int Length = 0, const uint8_t *Data = nullptr) override; // cCiSession
1111 void SetTimeOffset(double offset);
1112 };
1113
1115:cCiSession(SessionId, RI_DATE_TIME, Tc)
1116{
1117 dbgprotocol("New Date Time (session id %d)\n", SessionId);
1118}
1119
1121{
1122 m_timeOffset = (int) offset;
1123 dbgprotocol("New Time Offset: %i secs\n", m_timeOffset);
1124}
1125
1126static constexpr uint8_t DEC2BCD(uint8_t d)
1127 { return ((d / 10) << 4) + (d % 10); }
1128static constexpr uint8_t BYTE0(uint16_t a)
1129 { return static_cast<uint8_t>(a & 0xFF); }
1130static constexpr uint8_t BYTE1(uint16_t a)
1131 { return static_cast<uint8_t>((a >> 8) & 0xFF); }
1132
1134{
1135 time_t t = time(nullptr);
1136 struct tm tm_gmt {};
1137 struct tm tm_loc {};
1138
1139 // Avoid using signed time_t types
1140 if (m_timeOffset < 0)
1141 t -= (time_t)(-m_timeOffset);
1142 else
1143 t += (time_t)(m_timeOffset);
1144
1145 if (gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc)) {
1146 int Y = tm_gmt.tm_year;
1147 int M = tm_gmt.tm_mon + 1;
1148 int D = tm_gmt.tm_mday;
1149 int L = (M == 1 || M == 2) ? 1 : 0;
1150 int MJD = 14956 + D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001);
1151 uint16_t mjd = htons(MJD);
1152 int16_t local_offset = htons(tm_loc.tm_gmtoff / 60);
1153 std::vector<uint8_t> T {
1154 BYTE0(mjd),
1155 BYTE1(mjd),
1156 DEC2BCD(tm_gmt.tm_hour),
1157 DEC2BCD(tm_gmt.tm_min),
1158 DEC2BCD(tm_gmt.tm_sec),
1159 BYTE0(local_offset),
1160 BYTE1(local_offset)
1161 };
1162
1163 dbgprotocol("%d: ==> Date Time\n", SessionId());
1165 //XXX return value of all SendData() calls???
1166 return true;
1167 }
1168 return false;
1169}
1170
1171bool cCiDateTime::Process(int Length, const uint8_t *Data)
1172{
1173 if (Data) {
1174 int Tag = GetTag(Length, &Data);
1175 switch (Tag) {
1176 case AOT_DATE_TIME_ENQ: {
1177 m_interval = 0;
1178 int l = 0;
1179 const uint8_t *d = GetData(Data, l);
1180 if (l > 0)
1181 m_interval = *d;
1182 dbgprotocol("%d: <== Date Time Enq, interval = %d\n", SessionId(), m_interval);
1183 m_lastTime = time(nullptr);
1184 return SendDateTime();
1185 }
1186 break;
1187 default: esyslog("ERROR: CI date time: unknown tag %06X", Tag);
1188 return false;
1189 }
1190 }
1191 else if (m_interval && time(nullptr) - m_lastTime > m_interval) {
1192 m_lastTime = time(nullptr);
1193 return SendDateTime();
1194 }
1195 return true;
1196}
1197
1198// --- cCiMMI ----------------------------------------------------------------
1199
1200// Close MMI Commands:
1201
1202enum CLOSE_MMI : std::uint8_t {
1205};
1206
1207// Display Control Commands:
1208
1209enum DISPLAY_CONTROL : std::uint8_t {
1215};
1216
1217// MMI Modes:
1218
1219enum MMI_MODES : std::uint8_t {
1223};
1224
1225// Display Reply IDs:
1226
1227enum DISPLAY_REPLY_IDS : std::uint8_t {
1236};
1237
1238// Enquiry Flags:
1239
1240static constexpr uint8_t EF_BLIND { 0x01 };
1241
1242// Answer IDs:
1243
1244enum ANSWER_IDS : std::uint8_t {
1247};
1248
1249class cCiMMI : public cCiSession {
1250private:
1251 char *GetText(int &Length, const uint8_t **Data);
1254public:
1256 ~cCiMMI() override;
1257 bool Process(int Length = 0, const uint8_t *Data = nullptr) override; // cCiSession
1258 bool HasUserIO(void) override { return m_menu || m_enquiry; } // cCiSession
1259 cCiMenu *Menu(void);
1260 cCiEnquiry *Enquiry(void);
1261 bool SendMenuAnswer(uint8_t Selection);
1262 bool SendAnswer(const char *Text);
1263 };
1264
1266:cCiSession(SessionId, RI_MMI, Tc)
1267{
1268 dbgprotocol("New MMI (session id %d)\n", SessionId);
1269 m_menu = nullptr;
1270 m_enquiry = nullptr;
1271}
1272
1274{
1275 delete m_menu;
1276 delete m_enquiry;
1277}
1278
1279char *cCiMMI::GetText(int &Length, const uint8_t **Data)
1287{
1288 int Tag = GetTag(Length, Data);
1289 if (Tag == AOT_TEXT_LAST) {
1290 char *s = GetString(Length, Data);
1291 dbgprotocol("%d: <== Text Last '%s'\n", SessionId(), s);
1292 return s;
1293 }
1294 esyslog("CI MMI: unexpected text tag: %06X", Tag);
1295 return nullptr;
1296}
1297
1298bool cCiMMI::Process(int Length, const uint8_t *Data)
1299{
1300 if (Data) {
1301 int Tag = GetTag(Length, &Data);
1302 switch (Tag) {
1303 case AOT_DISPLAY_CONTROL: {
1304 dbgprotocol("%d: <== Display Control\n", SessionId());
1305 int l = 0;
1306 const uint8_t *d = GetData(Data, l);
1307 if (l > 0) {
1308 switch (*d) {
1309 case DCC_SET_MMI_MODE:
1310 if (l == 2 && *++d == MM_HIGH_LEVEL) {
1311 struct tDisplayReply { uint8_t m_id; uint8_t m_mode; };
1312 tDisplayReply dr {};
1313 dr.m_id = DRI_MMI_MODE_ACK;
1314 dr.m_mode = MM_HIGH_LEVEL;
1315 dbgprotocol("%d: ==> Display Reply\n", SessionId());
1316 SendData(AOT_DISPLAY_REPLY, 2, (uint8_t *)&dr);
1317 }
1318 break;
1319 default: esyslog("CI MMI: unsupported display control command %02X", *d);
1320 return false;
1321 }
1322 }
1323 }
1324 break;
1325 case AOT_LIST_LAST:
1326 case AOT_MENU_LAST: {
1327 dbgprotocol("%d: <== Menu Last\n", SessionId());
1328 delete m_menu;
1329 m_menu = new cCiMenu(this, Tag == AOT_MENU_LAST);
1330 int l = 0;
1331 const uint8_t *d = GetData(Data, l);
1332 if (l > 0) {
1333 // since the specification allows choiceNb to be undefined it is useless, so let's just skip it:
1334 d++;
1335 l--;
1336 if (l > 0) m_menu->m_titleText = GetText(l, &d);
1337 if (l > 0) m_menu->m_subTitleText = GetText(l, &d);
1338 if (l > 0) m_menu->m_bottomText = GetText(l, &d);
1339 while (l > 0) {
1340 char *s = GetText(l, &d);
1341 if (s) {
1342 if (!m_menu->AddEntry(s))
1343 free(s);
1344 }
1345 else {
1346 break;
1347 }
1348 }
1349 }
1350 }
1351 break;
1352 case AOT_ENQ: {
1353 dbgprotocol("%d: <== Enq\n", SessionId());
1354 delete m_enquiry;
1355 m_enquiry = new cCiEnquiry(this);
1356 int l = 0;
1357 const uint8_t *d = GetData(Data, l);
1358 if (l > 0) {
1359 uint8_t blind = *d++;
1360 //XXX GetByte()???
1361 l--;
1362 m_enquiry->m_blind = ((blind & EF_BLIND) != 0);
1364 l--;
1365 // I really wonder why there is no text length field here...
1367 }
1368 }
1369 break;
1370 case AOT_CLOSE_MMI: {
1371 int l = 0;
1372 const uint8_t *d = GetData(Data, l);
1373
1374 if(l > 0){
1375 switch(*d){
1377 dbgprotocol("%d <== Menu Close: immediate\n", SessionId());
1378 break;
1379 case CLOSE_MMI_DELAY:
1380 dbgprotocol("%d <== Menu Close: delay\n", SessionId());
1381 break;
1382 default: esyslog("ERROR: CI MMI: unknown close_mmi_cmd_id %02X", *d);
1383 return false;
1384 }
1385 }
1386
1387 break;
1388 }
1389 default: esyslog("ERROR: CI MMI: unknown tag %06X", Tag);
1390 return false;
1391 }
1392 }
1393 return true;
1394}
1395
1397{
1398 cCiMenu *m = m_menu;
1399 m_menu = nullptr;
1400 return m;
1401}
1402
1404{
1405 cCiEnquiry *e = m_enquiry;
1406 m_enquiry = nullptr;
1407 return e;
1408}
1409
1410bool cCiMMI::SendMenuAnswer(uint8_t Selection)
1411{
1412 dbgprotocol("%d: ==> Menu Answ\n", SessionId());
1413 SendData(AOT_MENU_ANSW, 1, &Selection);
1414 //XXX return value of all SendData() calls???
1415 return true;
1416}
1417
1418// Define protocol structure
1419extern "C" {
1420 struct tAnswer { uint8_t m_id; char m_text[256]; };
1421}
1422
1423bool cCiMMI::SendAnswer(const char *Text)
1424{
1425 dbgprotocol("%d: ==> Answ\n", SessionId());
1426 tAnswer answer {};
1427 answer.m_id = Text ? AI_ANSWER : AI_CANCEL;
1428 if (Text) {
1429 strncpy(answer.m_text, Text, sizeof(answer.m_text) - 1);
1430 answer.m_text[255] = '\0';
1431 }
1432 SendData(AOT_ANSW, Text ? strlen(Text) + 1 : 1, (uint8_t *)&answer);
1433 //XXX return value of all SendData() calls???
1434 return true;
1435}
1436
1437// --- cCiMenu ---------------------------------------------------------------
1438
1439cCiMenu::cCiMenu(cCiMMI *MMI, bool Selectable)
1440 : m_mmi(MMI),
1441 m_selectable(Selectable)
1442{
1443}
1444
1446{
1447 free(m_titleText);
1448 free(m_subTitleText);
1449 free(m_bottomText);
1450 for (int i = 0; i < m_numEntries; i++)
1451 free(m_entries[i]);
1452}
1453
1455{
1457 m_entries[m_numEntries++] = s;
1458 return true;
1459 }
1460 return false;
1461}
1462
1463bool cCiMenu::Select(int Index)
1464{
1465 if (m_mmi && -1 <= Index && Index < m_numEntries)
1466 return m_mmi->SendMenuAnswer(Index + 1);
1467 return false;
1468}
1469
1471{
1472 return Select(-1);
1473}
1474
1475// --- cCiEnquiry ------------------------------------------------------------
1476
1478{
1479 free(m_text);
1480}
1481
1482bool cCiEnquiry::Reply(const char *s)
1483{
1484 return m_mmi ? m_mmi->SendAnswer(s) : false;
1485}
1486
1488{
1489 return Reply(nullptr);
1490}
1491
1492// --- cCiCaPmt --------------------------------------------------------------
1493
1494// Ca Pmt Cmd Ids:
1495
1496enum CPCI_IDS : std::uint8_t {
1501};
1502
1503cCiCaPmt::cCiCaPmt(int ProgramNumber, uint8_t cplm)
1504{
1505 m_capmt[m_length++] = cplm; // ca_pmt_list_management
1506 m_capmt[m_length++] = (ProgramNumber >> 8) & 0xFF;
1507 m_capmt[m_length++] = ProgramNumber & 0xFF;
1508 m_capmt[m_length++] = 0x01; // version_number, current_next_indicator - apparently vn doesn't matter, but cni must be 1
1509
1510 // program_info_length
1511 m_infoLengthPos = m_length;// NOLINT(cppcoreguidelines-prefer-member-initializer)
1512 m_capmt[m_length++] = 0x00;
1513 m_capmt[m_length++] = 0x00;
1514}
1515
1517{
1518 if (m_length + 5 > int(sizeof(m_capmt)))
1519 {
1520 esyslog("ERROR: buffer overflow in CA_PMT");
1521 return;
1522 }
1523
1524 m_capmt[m_length++] = type & 0xFF;
1525 m_capmt[m_length++] = (pid >> 8) & 0xFF;
1526 m_capmt[m_length++] = pid & 0xFF;
1527
1528 // ES_info_length
1530 m_capmt[m_length++] = 0x00;
1531 m_capmt[m_length++] = 0x00;
1532}
1533
1551void cCiCaPmt::AddCaDescriptor(int ca_system_id, int ca_pid, int data_len,
1552 const uint8_t *data)
1553{
1554 if (!m_infoLengthPos)
1555 {
1556 esyslog("ERROR: adding CA descriptor without program/stream!");
1557 return;
1558 }
1559
1560 if (m_length + data_len + 7 > int(sizeof(m_capmt)))
1561 {
1562 esyslog("ERROR: buffer overflow in CA_PMT");
1563 return;
1564 }
1565
1566 // We are either at start of program descriptors or stream descriptors.
1567 if (m_infoLengthPos + 2 == m_length)
1568 m_capmt[m_length++] = CPCI_OK_DESCRAMBLING; // ca_pmt_cmd_id
1569
1570 m_capmt[m_length++] = 0x09; // CA descriptor tag
1571 m_capmt[m_length++] = 4 + data_len; // descriptor length
1572
1573 m_capmt[m_length++] = (ca_system_id >> 8) & 0xFF;
1574 m_capmt[m_length++] = ca_system_id & 0xFF;
1575 m_capmt[m_length++] = (ca_pid >> 8) & 0xFF;
1576 m_capmt[m_length++] = ca_pid & 0xFF;
1577
1578 if (data_len > 0)
1579 {
1580 memcpy(&m_capmt[m_length], data, data_len);
1581 m_length += data_len;
1582 }
1583
1584 // update program_info_length/ES_info_length
1585 int l = m_length - m_infoLengthPos - 2;
1586 m_capmt[m_infoLengthPos] = (l >> 8) & 0xFF;
1587 m_capmt[m_infoLengthPos + 1] = l & 0xFF;
1588}
1589
1590// -- cLlCiHandler -------------------------------------------------------------
1591
1592cLlCiHandler::cLlCiHandler(int Fd, int NumSlots)
1593 : m_fdCa(Fd),
1594 m_numSlots(NumSlots),
1595 m_tpl(new cCiTransportLayer(Fd, m_numSlots))
1596{
1597}
1598
1600{
1601 cMutexLock MutexLock(&m_mutex);
1602 for (auto & session : m_sessions)
1603 delete session;
1604 delete m_tpl;
1605 close(m_fdCa);
1606}
1607
1609{
1610 int fd_ca = open(FileName, O_RDWR);
1611 if (fd_ca >= 0)
1612 {
1613 ca_caps_t Caps;
1614 if (ioctl(fd_ca, CA_GET_CAP, &Caps) == 0)
1615 {
1616 int NumSlots = Caps.slot_num;
1617 if (NumSlots > 0)
1618 {
1619 if (Caps.slot_type & CA_CI_LINK)
1620 return new cLlCiHandler(fd_ca, NumSlots);
1621 if (Caps.slot_type & CA_CI)
1622 return new cHlCiHandler(fd_ca, NumSlots);
1623 isyslog("CAM doesn't support either high or low level CI,"
1624 " Caps.slot_type=%i", Caps.slot_type);
1625 }
1626 else
1627 {
1628 esyslog("ERROR: no CAM slots found");
1629 }
1630 }
1631 else
1632 {
1633 LOG_ERROR_STR(FileName);
1634 }
1635 close(fd_ca);
1636 }
1637 return nullptr;
1638}
1639
1640int cLlCiHandler::ResourceIdToInt(const uint8_t *Data)
1641{
1642 return (ntohl(*(int *)Data));
1643}
1644
1645bool cLlCiHandler::Send(uint8_t Tag, int SessionId, int ResourceId, int Status)
1646{
1647 std::vector<uint8_t> buffer {Tag, 0x00} ; // 0x00 will be replaced with length
1648 if (Status >= 0)
1649 buffer.push_back(Status);
1650 if (ResourceId) {
1651 buffer.push_back((ResourceId >> 24) & 0xFF);
1652 buffer.push_back((ResourceId >> 16) & 0xFF);
1653 buffer.push_back((ResourceId >> 8) & 0xFF);
1654 buffer.push_back( ResourceId & 0xFF);
1655 }
1656 buffer.push_back((SessionId >> 8) & 0xFF);
1657 buffer.push_back( SessionId & 0xFF);
1658 buffer[1] = buffer.size() - 2; // length
1659 return m_tc && m_tc->SendData(buffer) == OK;
1660}
1661
1663{
1664 for (auto & session : m_sessions) {
1665 if (session && session->SessionId() == SessionId)
1666 return session;
1667 }
1668 return nullptr;
1669}
1670
1672{
1673 for (auto & session : m_sessions) {
1674 if (session && session->Tc()->Slot() == Slot && session->ResourceId() == ResourceId)
1675 return session;
1676 }
1677 return nullptr;
1678}
1679
1681{
1682 if (!GetSessionByResourceId(ResourceId, m_tc->Slot())) {
1683 for (int i = 0; i < MAX_CI_SESSION; i++) {
1684 if (!m_sessions[i]) {
1685 switch (ResourceId) {
1686 case RI_RESOURCE_MANAGER: return m_sessions[i] = new cCiResourceManager(i + 1, m_tc);
1689 return m_sessions[i] = new cCiConditionalAccessSupport(i + 1, m_tc);
1690 case RI_HOST_CONTROL: break; //XXX
1691 case RI_DATE_TIME: return m_sessions[i] = new cCiDateTime(i + 1, m_tc);
1692 case RI_MMI: return m_sessions[i] = new cCiMMI(i + 1, m_tc);
1693 }
1694 }
1695 }
1696 }
1697 return nullptr;
1698}
1699
1700bool cLlCiHandler::OpenSession(int Length, const uint8_t *Data)
1701{
1702 if (Length == 6 && *(Data + 1) == 0x04) {
1703 int ResourceId = ResourceIdToInt(Data + 2);
1704 dbgprotocol("OpenSession %08X\n", ResourceId);
1705 switch (ResourceId) {
1709 case RI_HOST_CONTROL:
1710 case RI_DATE_TIME:
1711 case RI_MMI:
1712 {
1713 cCiSession *Session = CreateSession(ResourceId);
1714 if (Session)
1715 {
1717 Session->ResourceId(), SS_OK);
1718 return true;
1719 }
1720 esyslog("ERROR: can't create session for resource identifier: %08X",
1721 ResourceId);
1722 break;
1723 }
1724 default: esyslog("ERROR: unknown resource identifier: %08X", ResourceId);
1725 }
1726 }
1727 return false;
1728}
1729
1731{
1732 dbgprotocol("CloseSession %08X\n", SessionId);
1733 cCiSession *Session = GetSessionBySessionId(SessionId);
1734 if (Session && m_sessions[SessionId - 1] == Session) {
1735 delete Session;
1736 m_sessions[SessionId - 1] = nullptr;
1737 Send(ST_CLOSE_SESSION_RESPONSE, SessionId, 0, SS_OK);
1738 return true;
1739 }
1740
1741 esyslog("ERROR: unknown session id: %d", SessionId);
1743 return false;
1744}
1745
1747{
1748 int result = 0;
1749 for (auto & session : m_sessions) {
1750 if (session && session->Tc()->Slot() == Slot) {
1751 CloseSession(session->SessionId());
1752 result++;
1753 }
1754 }
1755 return result;
1756}
1757
1759{
1760 bool result = true;
1761 cMutexLock MutexLock(&m_mutex);
1762
1763 for (int Slot = 0; Slot < m_numSlots; Slot++)
1764 {
1765 m_tc = m_tpl->Process(Slot);
1766 if (m_tc)
1767 {
1768 int Length = 0;
1769 const uint8_t *Data = m_tc->Data(Length);
1770 if (Data && Length > 1)
1771 {
1772 switch (*Data)
1773 {
1774 case ST_SESSION_NUMBER:
1775 if (Length > 4)
1776 {
1777 int SessionId = ntohs(*(short *)&Data[2]);
1778 cCiSession *Session = GetSessionBySessionId(SessionId);
1779 if (Session)
1780 {
1781 Session->Process(Length - 4, Data + 4);
1782 if (Session->ResourceId() == RI_APPLICATION_INFORMATION)
1783 {
1784#if 0
1785 esyslog("Test: %x",
1786 ((cCiApplicationInformation*)Session)->GetApplicationManufacturer());
1787#endif
1788 }
1789 }
1790 else
1791 {
1792 esyslog("ERROR: unknown session id: %d", SessionId);
1793 }
1794 }
1795 break;
1796
1798 OpenSession(Length, Data);
1799 break;
1800
1802 if (Length == 4)
1803 CloseSession(ntohs(*(short *)&Data[2]));
1804 break;
1805
1806 case ST_CREATE_SESSION_RESPONSE: //XXX fall through to default
1807 case ST_CLOSE_SESSION_RESPONSE: //XXX fall through to default
1808 default:
1809 esyslog("ERROR: unknown session tag: %02X", *Data);
1810 }
1811 }
1812 }
1813 else if (CloseAllSessions(Slot))
1814 {
1815 m_tpl->ResetSlot(Slot);
1816 result = false;
1817 }
1818 else if (m_tpl->ModuleReady(Slot))
1819 {
1820 dbgprotocol("Module ready in slot %d\n", Slot);
1821 m_tpl->NewConnection(Slot);
1822 }
1823 }
1824
1825 bool UserIO = false;
1826 m_needCaPmt = false;
1827 for (auto & session : m_sessions)
1828 {
1829 if (session && session->Process())
1830 {
1831 UserIO |= session->HasUserIO();
1832 if (session->ResourceId() == RI_CONDITIONAL_ACCESS_SUPPORT)
1833 {
1834 auto *cas = dynamic_cast<cCiConditionalAccessSupport *>(session);
1835 if (cas == nullptr)
1836 continue;
1837 m_needCaPmt |= cas->NeedCaPmt();
1838 }
1839 }
1840 }
1841 m_hasUserIO = UserIO;
1842
1843 if (m_newCaSupport)
1844 m_newCaSupport = result = false; // triggers new SetCaPmt at caller!
1845 return result;
1846}
1847
1849{
1850 cMutexLock MutexLock(&m_mutex);
1852 return api ? api->EnterMenu() : false;
1853}
1854
1856{
1857 cMutexLock MutexLock(&m_mutex);
1858 for (int Slot = 0; Slot < m_numSlots; Slot++) {
1859 auto *mmi = dynamic_cast<cCiMMI *>(GetSessionByResourceId(RI_MMI, Slot));
1860 if (mmi)
1861 return mmi->Menu();
1862 }
1863 return nullptr;
1864}
1865
1867{
1868 cMutexLock MutexLock(&m_mutex);
1869 for (int Slot = 0; Slot < m_numSlots; Slot++) {
1870 auto *mmi = dynamic_cast<cCiMMI *>(GetSessionByResourceId(RI_MMI, Slot));
1871 if (mmi)
1872 return mmi->Enquiry();
1873 }
1874 return nullptr;
1875}
1876
1878 {
1879 static dvbca_vector empty {};
1880 cMutexLock MutexLock(&m_mutex);
1882 return cas ? cas->GetCaSystemIds() : empty;
1883}
1884
1885bool cLlCiHandler::SetCaPmt(cCiCaPmt &CaPmt, int Slot)
1886{
1887 cMutexLock MutexLock(&m_mutex);
1889 return cas && cas->SendPMT(CaPmt);
1890}
1891
1892void cLlCiHandler::SetTimeOffset(double offset_in_seconds)
1893{
1894 cMutexLock MutexLock(&m_mutex);
1895 cCiDateTime *dt = nullptr;
1896
1897 for (uint i = 0; i < (uint) NumSlots(); i++)
1898 {
1899 dt = dynamic_cast<cCiDateTime*>(GetSessionByResourceId(RI_DATE_TIME, i));
1900 if (dt)
1901 dt->SetTimeOffset(offset_in_seconds);
1902 }
1903}
1904
1906{
1907 cMutexLock MutexLock(&m_mutex);
1908 CloseAllSessions(Slot);
1909 return m_tpl->ResetSlot(Slot);
1910}
1911
1913{
1914 return sConnected;
1915}
1916
1917// -- cHlCiHandler -------------------------------------------------------------
1918
1919cHlCiHandler::cHlCiHandler(int Fd, int NumSlots)
1920 : m_fdCa(Fd),
1921 m_numSlots(NumSlots)
1922{
1923 esyslog("New High level CI handler");
1924}
1925
1927{
1928 cMutexLock MutexLock(&m_mutex);
1929 close(m_fdCa);
1930}
1931
1932int cHlCiHandler::CommHL(unsigned tag, unsigned function, struct ca_msg *msg) const
1933{
1934 if (tag) {
1935 msg->msg[2] = tag & 0xff;
1936 msg->msg[1] = (tag & 0xff00) >> 8;
1937 msg->msg[0] = (tag & 0xff0000) >> 16;
1938 esyslog("Sending message=[%02x %02x %02x ]",
1939 msg->msg[0], msg->msg[1], msg->msg[2]);
1940 }
1941
1942 return ioctl(m_fdCa, function, msg);
1943}
1944
1945int cHlCiHandler::GetData(unsigned tag, struct ca_msg *msg)
1946{
1947 return CommHL(tag, CA_GET_MSG, msg);
1948}
1949
1950int cHlCiHandler::SendData(unsigned tag, struct ca_msg *msg)
1951{
1952 return CommHL(tag, CA_SEND_MSG, msg);
1953}
1954
1956{
1957 cMutexLock MutexLock(&m_mutex);
1958
1959 struct ca_msg msg {};
1960 switch(m_state) {
1961 case 0:
1962 // Get CA_system_ids
1963 /* Enquire */
1964 if ((SendData(AOT_CA_INFO_ENQ, &msg)) < 0) {
1965 esyslog("HLCI communication failed");
1966 } else {
1967 dbgprotocol("==> Ca Info Enquiry");
1968 /* Receive */
1969 if ((GetData(AOT_CA_INFO, &msg)) < 0) {
1970 esyslog("HLCI communication failed");
1971 } else {
1972 QString message("Debug: ");
1973 for(int i = 0; i < 20; i++) {
1974 message += QString("%1 ").arg(msg.msg[i]);
1975 }
1976 LOG(VB_GENERAL, LOG_DEBUG, message);
1977 dbgprotocol("<== Ca Info");
1978 int l = msg.msg[3];
1979 const uint8_t *d = &msg.msg[4];
1980 while (l > 1) {
1981 unsigned short id = ((unsigned short)(*d) << 8) | *(d + 1);
1982 dbgprotocol(" %04X", id);
1983 d += 2;
1984 l -= 2;
1985
1986 // Insert before the last element.
1987 m_caSystemIds.emplace_back(id);
1988 }
1989 dbgprotocol("\n");
1990 }
1991 m_state = 1;
1992 break;
1993 }
1994 }
1995
1996 bool result = true;
1997
1998 return result;
1999}
2000
2001bool cHlCiHandler::EnterMenu(int /*Slot*/)
2002{
2003 return false;
2004}
2005
2007{
2008 return nullptr;
2009}
2010
2012{
2013 return nullptr;
2014}
2015
2017{
2018 return m_caSystemIds;
2019}
2020
2021bool cHlCiHandler::SetCaPmt(cCiCaPmt &CaPmt, int /*Slot*/)
2022{
2023 cMutexLock MutexLock(&m_mutex);
2024 struct ca_msg msg {};
2025
2026 esyslog("Setting CA PMT.");
2027 m_state = 2;
2028
2029 msg.msg[3] = CaPmt.m_length;
2030
2031 if (CaPmt.m_length > (256 - 4))
2032 {
2033 esyslog("CA message too long");
2034 return false;
2035 }
2036
2037 memcpy(&msg.msg[4], CaPmt.m_capmt, CaPmt.m_length);
2038
2039 if ((SendData(AOT_CA_PMT, &msg)) < 0) {
2040 esyslog("HLCI communication failed");
2041 return false;
2042 }
2043
2044 return true;
2045}
2046
2047bool cHlCiHandler::Reset(int /*Slot*/) const
2048{
2049 if ((ioctl(m_fdCa, CA_RESET)) < 0) {
2050 esyslog("ioctl CA_RESET failed.");
2051 return false;
2052 }
2053 return true;
2054}
2055
2057{
2058 return m_state == 1;
2059}
uint16_t m_applicationManufacturer
Definition: dvbci.cpp:948
~cCiApplicationInformation() override
Definition: dvbci.cpp:973
cCiApplicationInformation(int SessionId, cCiTransportConnection *Tc)
Definition: dvbci.cpp:961
uint16_t m_manufacturerCode
Definition: dvbci.cpp:949
char * GetApplicationString()
Definition: dvbci.cpp:956
bool Process(int Length=0, const uint8_t *Data=nullptr) override
Definition: dvbci.cpp:978
uint16_t GetApplicationManufacturer() const
Definition: dvbci.cpp:957
uint16_t GetManufacturerCode() const
Definition: dvbci.cpp:958
void AddCaDescriptor(int ca_system_id, int ca_pid, int data_len, const uint8_t *data)
Definition: dvbci.cpp:1551
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:1516
cCiCaPmt(int ProgramNumber, uint8_t cplm=CPLM_ONLY)
Definition: dvbci.cpp:1503
bool SendPMT(const cCiCaPmt &CaPmt)
Definition: dvbci.cpp:1090
cCiConditionalAccessSupport(int SessionId, cCiTransportConnection *Tc)
Definition: dvbci.cpp:1042
dvbca_vector m_caSystemIds
Definition: dvbci.cpp:1032
dvbca_vector GetCaSystemIds(void)
Definition: dvbci.cpp:1037
bool Process(int Length=0, const uint8_t *Data=nullptr) override
Definition: dvbci.cpp:1049
bool NeedCaPmt(void) const
Definition: dvbci.cpp:1039
bool Process(int Length=0, const uint8_t *Data=nullptr) override
Definition: dvbci.cpp:1171
cCiDateTime(int SessionId, cCiTransportConnection *Tc)
Definition: dvbci.cpp:1114
int m_timeOffset
Definition: dvbci.cpp:1106
void SetTimeOffset(double offset)
Definition: dvbci.cpp:1120
int m_interval
Definition: dvbci.cpp:1104
bool SendDateTime(void)
Definition: dvbci.cpp:1133
time_t m_lastTime
Definition: dvbci.cpp:1105
bool Reply(const char *s)
Definition: dvbci.cpp:1482
int m_expectedLength
Definition: dvbci.h:103
~cCiEnquiry()
Definition: dvbci.cpp:1477
bool Cancel(void)
Definition: dvbci.cpp:1487
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:1608
cCiMMI(int SessionId, cCiTransportConnection *Tc)
Definition: dvbci.cpp:1265
cCiMenu * Menu(void)
Definition: dvbci.cpp:1396
bool HasUserIO(void) override
Definition: dvbci.cpp:1258
bool Process(int Length=0, const uint8_t *Data=nullptr) override
Definition: dvbci.cpp:1298
bool SendMenuAnswer(uint8_t Selection)
Definition: dvbci.cpp:1410
cCiMenu * m_menu
Definition: dvbci.cpp:1252
~cCiMMI() override
Definition: dvbci.cpp:1273
bool SendAnswer(const char *Text)
Definition: dvbci.cpp:1423
cCiEnquiry * m_enquiry
Definition: dvbci.cpp:1253
char * GetText(int &Length, const uint8_t **Data)
Definition: dvbci.cpp:1279
cCiEnquiry * Enquiry(void)
Definition: dvbci.cpp:1403
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:1463
bool AddEntry(char *s)
Definition: dvbci.cpp:1454
cCiMenu(cCiMMI *MMI, bool Selectable)
Definition: dvbci.cpp:1439
char * m_bottomText
Definition: dvbci.h:80
~cCiMenu()
Definition: dvbci.cpp:1445
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:1470
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:2056
int m_state
Definition: dvbci.h:208
int SendData(unsigned tag, struct ca_msg *msg)
Definition: dvbci.cpp:1950
bool EnterMenu(int Slot) override
Definition: dvbci.cpp:2001
cHlCiHandler(int Fd, int NumSlots)
Definition: dvbci.cpp:1919
cCiMenu * GetMenu(void) override
Definition: dvbci.cpp:2006
cCiEnquiry * GetEnquiry(void) override
Definition: dvbci.cpp:2011
dvbca_vector GetCaSystemIds(int Slot) override
Definition: dvbci.cpp:2016
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:1955
int GetData(unsigned tag, struct ca_msg *msg)
Definition: dvbci.cpp:1945
~cHlCiHandler() override
Definition: dvbci.cpp:1926
int CommHL(unsigned tag, unsigned function, struct ca_msg *msg) const
Definition: dvbci.cpp:1932
bool Reset(int Slot) const
Definition: dvbci.cpp:2047
bool CloseSession(int SessionId)
Definition: dvbci.cpp:1730
bool OpenSession(int Length, const uint8_t *Data)
Definition: dvbci.cpp:1700
int CloseAllSessions(int Slot)
Definition: dvbci.cpp:1746
bool Send(uint8_t Tag, int SessionId, int ResourceId=0, int Status=-1)
Definition: dvbci.cpp:1645
bool m_needCaPmt
Definition: dvbci.h:167
cCiMenu * GetMenu(void) override
Definition: dvbci.cpp:1855
bool m_hasUserIO
Definition: dvbci.h:166
~cLlCiHandler() override
Definition: dvbci.cpp:1599
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:1866
bool Process(void) override
Definition: dvbci.cpp:1758
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:1892
cLlCiHandler(int Fd, int NumSlots)
Definition: dvbci.cpp:1592
cCiSession * CreateSession(int ResourceId)
Definition: dvbci.cpp:1680
bool EnterMenu(int Slot) override
Definition: dvbci.cpp:1848
static int ResourceIdToInt(const uint8_t *Data)
Definition: dvbci.cpp:1640
static bool connected()
Definition: dvbci.cpp:1912
int m_fdCa
Definition: dvbci.h:163
cCiSession * GetSessionByResourceId(int ResourceId, int Slot)
Definition: dvbci.cpp:1671
int m_numSlots
Definition: dvbci.h:164
dvbca_vector GetCaSystemIds(int Slot) override
Definition: dvbci.cpp:1877
bool Reset(int Slot)
Definition: dvbci.cpp:1905
cCiSession * GetSessionBySessionId(int SessionId)
Definition: dvbci.cpp:1662
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: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
#define close
Definition: compat.h:39
static bool sConnected
Definition: dvbci.cpp:64
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 char * GetString(int &Length, const uint8_t **Data)
Definition: dvbci.cpp:178
static constexpr uint8_t DATA_INDICATOR
Definition: dvbci.cpp:248
CLOSE_MMI
Definition: dvbci.cpp:1202
@ CLOSE_MMI_DELAY
Definition: dvbci.cpp:1204
@ CLOSE_MMI_IMMEDIATE
Definition: dvbci.cpp:1203
static constexpr int SIZE_INDICATOR
Definition: dvbci.cpp:81
static constexpr uint8_t BYTE1(uint16_t a)
Definition: dvbci.cpp:1130
CPCI_IDS
Definition: dvbci.cpp:1496
@ CPCI_OK_MMI
Definition: dvbci.cpp:1498
@ CPCI_QUERY
Definition: dvbci.cpp:1499
@ CPCI_NOT_SELECTED
Definition: dvbci.cpp:1500
@ CPCI_OK_DESCRAMBLING
Definition: dvbci.cpp:1497
static uint8_t * SetLength(uint8_t *Data, int Length)
Definition: dvbci.cpp:117
#define LOG_ERROR_STR(s)
Definition: dvbci.cpp:57
static constexpr size_t MAX_CI_CONNECT
Definition: dvbci.cpp:596
#define dsyslog(a...)
Definition: dvbci.cpp:54
DISPLAY_CONTROL
Definition: dvbci.cpp:1209
@ DCC_DISPLAY_CHARACTER_TABLE_LIST
Definition: dvbci.cpp:1211
@ DCC_OVERLAY_GRAPHICS_CHARACTERISTICS
Definition: dvbci.cpp:1213
@ DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS
Definition: dvbci.cpp:1214
@ DCC_SET_MMI_MODE
Definition: dvbci.cpp:1210
@ DCC_INPUT_CHARACTER_TABLE_LIST
Definition: dvbci.cpp:1212
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:1240
static constexpr int MAX_TPDU_DATA
Definition: dvbci.cpp:246
DISPLAY_REPLY_IDS
Definition: dvbci.cpp:1227
@ DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS
Definition: dvbci.cpp:1232
@ DRI_UNKNOWN_DISPLAY_CONTROL_CMD
Definition: dvbci.cpp:1233
@ DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS
Definition: dvbci.cpp:1231
@ DRI_UNKNOWN_CHARACTER_TABLE
Definition: dvbci.cpp:1235
@ DRI_LIST_DISPLAY_CHARACTER_TABLES
Definition: dvbci.cpp:1229
@ DRI_LIST_INPUT_CHARACTER_TABLES
Definition: dvbci.cpp:1230
@ DRI_UNKNOWN_MMI_MODE
Definition: dvbci.cpp:1234
@ DRI_MMI_MODE_ACK
Definition: dvbci.cpp:1228
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:69
static ssize_t safe_read(int filedes, void *buffer, size_t size)
Definition: dvbci.cpp:83
static constexpr int TIMEOUT
Definition: dvbci.cpp:70
static bool sDebugProtocol
Definition: dvbci.cpp:63
static constexpr int CAM_READ_TIMEOUT
Definition: dvbci.cpp:456
MMI_MODES
Definition: dvbci.cpp:1219
@ MM_LOW_LEVEL_OVERLAY_GRAPHICS
Definition: dvbci.cpp:1221
@ MM_HIGH_LEVEL
Definition: dvbci.cpp:1220
@ MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS
Definition: dvbci.cpp:1222
#define esyslog(a...)
Definition: dvbci.cpp:52
static char * CopyString(int Length, const uint8_t *Data)
Definition: dvbci.cpp:166
static constexpr uint8_t DEC2BCD(uint8_t d)
Definition: dvbci.cpp:1126
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:67
#define isyslog(a...)
Definition: dvbci.cpp:53
static constexpr std::chrono::milliseconds POLL_INTERVAL
Definition: dvbci.cpp:573
static constexpr int ERROR
Definition: dvbci.cpp:71
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:95
static bool sDumpTPDUDataTransfer
Definition: dvbci.cpp:62
static constexpr uint8_t BYTE0(uint16_t a)
Definition: dvbci.cpp:1128
static constexpr time_t WRKRND_TIME_BEFORE_ENTER_MENU
Definition: dvbci.cpp:77
ANSWER_IDS
Definition: dvbci.cpp:1244
@ AI_ANSWER
Definition: dvbci.cpp:1246
@ AI_CANCEL
Definition: dvbci.cpp:1245
#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)
unsigned int uint
Definition: freesurround.h:24
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:1420
uint8_t m_id
Definition: dvbci.cpp:1420
State
Definition: zmserver.h:69