38#include <linux/dvb/ca.h>
39#include <netinet/in.h>
55#define esyslog(a...) LOG(VB_GENERAL, LOG_ERR, QString::asprintf(a))
56#define isyslog(a...) LOG(VB_DVBCAM, LOG_INFO, QString::asprintf(a))
57#define dsyslog(a...) LOG(VB_DVBCAM, LOG_DEBUG, QString::asprintf(a))
59#define LOG_ERROR esyslog("ERROR (%s,%d): %m", __FILE__, __LINE__)
60#define LOG_ERROR_STR(s) esyslog("ERROR: %s: %m", s)
70#define dbgprotocol(a...) if (sDebugProtocol) LOG(VB_DVBCAM, LOG_DEBUG, QString::asprintf(a))
72static constexpr int OK { 0 };
74static constexpr int ERROR { -2 };
86static ssize_t
safe_read(
int filedes,
void *buffer,
size_t size)
89 ssize_t
p =
read(filedes, buffer, size);
90 if (
p < 0 && (errno == EINTR || errno == EAGAIN)) {
91 dsyslog(
"EINTR while reading from file handle %d - retrying", filedes);
98static const uint8_t *
GetLength(
const uint8_t *Data,
int &Length)
112 int l = Length & ~SIZE_INDICATOR;
114 for (
int i = 0; i < l; i++)
115 Length = (Length << 8) | *Data++;
135 int n =
sizeof(Length);
136 for (
int i = n - 1; i >= 0; i--) {
137 int b = (Length >> (8 * i)) & 0xFF;
148static void SetLength(std::vector<uint8_t> &Data,
int Length)
152 Data.push_back(Length);
157 size_t len_offset = Data.size();
160 int n =
sizeof(Length);
161 for (
int i = n - 1; i >= 0; i--) {
162 int b = (Length >> (8 * i)) & 0xFF;
163 if ((len_offset != Data.size()) || b)
169static std::string
CopyString(
int Length,
const uint8_t *Data)
175 return {
reinterpret_cast<const char*
>(Data),
static_cast<size_t>(Length) };
178static std::string
GetString(
int &Length,
const uint8_t **Data)
187 if (Length > 0 && Data && *Data) {
191 Length -=
d - *Data + l;
215 esyslog(
"cMutex Lock inbalance detected");
219 pthread_mutex_unlock(&
m_mutex);
267 std::array<uint8_t,MAX_TPDU_SIZE>
m_data {0};
268 const uint8_t *
GetData(
const uint8_t *
Data,
int &Length)
const;
279 void Dump(
bool Outgoing);
282cTPDU::cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag,
int Length,
const uint8_t *Data)
307 esyslog(
"ERROR: illegal data length for TPDU tag 0x%02X: %d",
Tag, Length);
313 uint8_t *
p =
m_data.data() + 3;
317 memcpy(
p,
Data, Length);
321 esyslog(
"ERROR: illegal data length for TPDU tag 0x%02X: %d",
Tag, Length);
325 esyslog(
"ERROR: unknown TPDU tag: 0x%02X",
Tag);
334 esyslog(
"ERROR: attemp to write TPDU with zero size");
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'));
359 LOG(VB_DVBCAM, LOG_INFO, msg);
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);
366 LOG(VB_DVBCAM, LOG_INFO, msg);
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;
423 const uint8_t *
Data(
int &Length);
460 std::array<struct pollfd,1> pfd {};
462 pfd[0].events = POLLIN;
467 if (ret == -1 && (errno == EAGAIN || errno == EINTR))
473 (pfd[0].revents & POLLIN) &&
495 default:
return ERROR;
559 dsyslog(
"CAM: retrying to establish connection");
561 dsyslog(
"CAM: connection established");
580 auto curr_time = nowAsDuration<std::chrono::milliseconds>();
581 std::chrono::milliseconds msdiff = curr_time -
m_lastPoll;
602 std::array<cCiTransportConnection,MAX_CI_CONNECT>
m_tc;
623 dbgprotocol(
"Creating connection: slot %d, tcid %zd\n", Slot, i + 1);
625 if (
m_tc[i].CreateConnection() ==
OK)
636 if (ioctl(
m_fd, CA_RESET, 1 << Slot) != -1) {
640 esyslog(
"ERROR: can't reset CAM slot %d: %m", Slot);
647 ca_slot_info_t sinfo;
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);
657 for (
auto & conn :
m_tc) {
659 if (Tc->
Slot() == Slot) {
660 switch (Tc->
State()) {
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);
789 {
return SendData(Tag, Data.size(), Data.data()); };
797 virtual bool Process(
int Length = 0,
const uint8_t *Data =
nullptr);
801 : m_sessionId(SessionId),
802 m_resourceId(ResourceId),
816 if (Length >= 3 && Data && *Data) {
818 for (
int i = 0; i < 3; i++)
819 t = (
t << 8) | *(*Data)++;
829 return Length ? Data :
nullptr;
836 esyslog(
"ERROR: CAM: data length (%d) is negative", Length);
840 if ((Length > 0) && !Data)
842 esyslog(
"ERROR: CAM: Data pointer null");
846 std::vector<uint8_t> buffer {
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);
856 if (buffer.size() + Length >= buffer.capacity())
858 esyslog(
"ERROR: CAM: data length (%d) exceeds buffer size", Length);
864 buffer.insert(buffer.end(), Data, Data + Length);
870 [[maybe_unused]]
const uint8_t *Data)
882 bool Process(
int Length = 0,
const uint8_t *Data =
nullptr)
override;
895 int Tag =
GetTag(Length, &Data);
899 const std::array<const uint32_t,5> resources
909 reinterpret_cast<const uint8_t*
>(resources.data()));
917 const uint8_t *
d =
GetData(Data, l);
919 esyslog(
"CI resource manager: unexpected data");
925 esyslog(
"ERROR: CI resource manager: unexpected tag %06X in state %d", Tag,
m_state);
929 default:
esyslog(
"ERROR: CI resource manager: unknown tag %06X", Tag);
953 bool Process(
int Length = 0,
const uint8_t *Data =
nullptr)
override;
974 int Tag =
GetTag(Length, &Data);
979 const uint8_t *
d =
GetData(Data, l);
997 default:
esyslog(
"ERROR: CI application information: unknown tag %06X", Tag);
1028 bool Process(
int Length = 0,
const uint8_t *Data =
nullptr)
override;
1044 int Tag =
GetTag(Length, &Data);
1049 const uint8_t *
d =
GetData(Data, l);
1051 unsigned short id = ((
unsigned short)(*
d) << 8) | *(
d + 1);
1057#ifdef __cpp_lib_ranges_contains
1074 default:
esyslog(
"ERROR: CI conditional access support: unknown tag %06X", Tag);
1106 bool Process(
int Length = 0,
const uint8_t *Data =
nullptr)
override;
1123 {
return ((
d / 10) << 4) + (
d % 10); }
1125 {
return static_cast<uint8_t
>(a & 0xFF); }
1127 {
return static_cast<uint8_t
>((a >> 8) & 0xFF); }
1131 time_t
t = time(
nullptr);
1132 struct tm tm_gmt {};
1133 struct tm tm_loc {};
1141 if (gmtime_r(&
t, &tm_gmt) && localtime_r(&
t, &tm_loc)) {
1142 int Y = tm_gmt.tm_year;
1143 int M = tm_gmt.tm_mon + 1;
1144 int D = tm_gmt.tm_mday;
1145 int L = (M == 1 || M == 2) ? 1 : 0;
1146 int MJD = 14956 +
D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001);
1148 int16_t local_offset = htons(tm_loc.tm_gmtoff / 60);
1149 std::vector<uint8_t> T {
1155 BYTE0(local_offset),
1170 int Tag =
GetTag(Length, &Data);
1175 const uint8_t *
d =
GetData(Data, l);
1183 default:
esyslog(
"ERROR: CI date time: unknown tag %06X", Tag);
1247 std::string
GetText(
int &Length,
const uint8_t **Data);
1253 bool Process(
int Length = 0,
const uint8_t *Data =
nullptr)
override;
1284 int Tag =
GetTag(Length, Data);
1286 std::string s =
GetString(Length, Data);
1290 esyslog(
"CI MMI: unexpected text tag: %06X", Tag);
1297 int Tag =
GetTag(Length, &Data);
1302 const uint8_t *
d =
GetData(Data, l);
1307 struct tDisplayReply { uint8_t m_id; uint8_t m_mode; };
1308 tDisplayReply dr {};
1315 default:
esyslog(
"CI MMI: unsupported display control command %02X", *
d);
1327 const uint8_t *
d =
GetData(Data, l);
1352 const uint8_t *
d =
GetData(Data, l);
1354 uint8_t blind = *
d++;
1367 const uint8_t *
d =
GetData(Data, l);
1377 default:
esyslog(
"ERROR: CI MMI: unknown close_mmi_cmd_id %02X", *
d);
1384 default:
esyslog(
"ERROR: CI MMI: unknown tag %06X", Tag);
1424 strncpy(answer.m_text, Text,
sizeof(answer.m_text) - 1);
1425 answer.m_text[255] =
'\0';
1436 m_selectable(Selectable)
1443 if (
m_mmi && -1 <= Index && Index <
static_cast<int>(
m_entries.size()))
1462 return Reply(
nullptr);
1493 esyslog(
"ERROR: buffer overflow in CA_PMT");
1525 const uint8_t *data)
1529 esyslog(
"ERROR: adding CA descriptor without program/stream!");
1535 esyslog(
"ERROR: buffer overflow in CA_PMT");
1567 m_numSlots(NumSlots),
1583 int fd_ca = open(FileName, O_RDWR);
1587 if (ioctl(fd_ca, CA_GET_CAP, &Caps) == 0)
1592 if (Caps.slot_type & CA_CI_LINK)
1594 if (Caps.slot_type & CA_CI)
1596 isyslog(
"CAM doesn't support either high or low level CI,"
1597 " Caps.slot_type=%i", Caps.slot_type);
1601 esyslog(
"ERROR: no CAM slots found");
1615 return (ntohl(*(
int *)Data));
1620 std::vector<uint8_t> buffer {Tag, 0x00} ;
1622 buffer.push_back(Status);
1624 buffer.push_back((ResourceId >> 24) & 0xFF);
1625 buffer.push_back((ResourceId >> 16) & 0xFF);
1626 buffer.push_back((ResourceId >> 8) & 0xFF);
1627 buffer.push_back( ResourceId & 0xFF);
1629 buffer.push_back((SessionId >> 8) & 0xFF);
1630 buffer.push_back( SessionId & 0xFF);
1631 buffer[1] = buffer.size() - 2;
1638 if (session && session->SessionId() == SessionId)
1647 if (session && session->Tc()->Slot() == Slot && session->ResourceId() == ResourceId)
1658 switch (ResourceId) {
1675 if (Length == 6 && *(Data + 1) == 0x04) {
1678 switch (ResourceId) {
1693 esyslog(
"ERROR: can't create session for resource identifier: %08X",
1697 default:
esyslog(
"ERROR: unknown resource identifier: %08X", ResourceId);
1707 if (Session &&
m_sessions[SessionId - 1] == Session) {
1714 esyslog(
"ERROR: unknown session id: %d", SessionId);
1723 if (session && session->Tc()->Slot() == Slot) {
1736 for (
int Slot = 0; Slot <
m_numSlots; Slot++)
1742 const uint8_t *Data =
m_tc->
Data(Length);
1743 if (Data && Length > 1)
1750 int SessionId = ntohs(*(
short *)&Data[2]);
1754 Session->
Process(Length - 4, Data + 4);
1765 esyslog(
"ERROR: unknown session id: %d", SessionId);
1782 esyslog(
"ERROR: unknown session tag: %02X", *Data);
1798 bool UserIO =
false;
1802 if (session && session->Process())
1804 UserIO |= session->HasUserIO();
1831 for (
int Slot = 0; Slot <
m_numSlots; Slot++) {
1842 for (
int Slot = 0; Slot <
m_numSlots; Slot++) {
1862 return cas && cas->
SendPMT(CaPmt);
1894 m_numSlots(NumSlots)
1896 esyslog(
"New High level CI handler");
1908 msg->msg[2] = tag & 0xff;
1909 msg->msg[1] = (tag & 0xff00) >> 8;
1910 msg->msg[0] = (tag & 0xff0000) >> 16;
1911 esyslog(
"Sending message=[%02x %02x %02x ]",
1912 msg->msg[0], msg->msg[1], msg->msg[2]);
1915 return ioctl(
m_fdCa, function, msg);
1920 return CommHL(tag, CA_GET_MSG, msg);
1925 return CommHL(tag, CA_SEND_MSG, msg);
1932 struct ca_msg msg {};
1938 esyslog(
"HLCI communication failed");
1943 esyslog(
"HLCI communication failed");
1945 QString message(
"Debug: ");
1946 for(
int i = 0; i < 20; i++) {
1947 message += QString(
"%1 ").arg(msg.msg[i]);
1949 LOG(VB_GENERAL, LOG_DEBUG, message);
1952 const uint8_t *
d = &msg.msg[4];
1954 unsigned short id = ((
unsigned short)(*
d) << 8) | *(
d + 1);
1997 struct ca_msg msg {};
2006 esyslog(
"CA message too long");
2013 esyslog(
"HLCI communication failed");
2022 if ((ioctl(
m_fdCa, CA_RESET)) < 0) {
2023 esyslog(
"ioctl CA_RESET failed.");
void AddCaDescriptor(int ca_system_id, int ca_pid, int data_len, const uint8_t *data)
uint8_t m_capmt[2048]
XXX is there a specified maximum?
void AddElementaryStream(int type, int pid)
cCiCaPmt(int ProgramNumber, uint8_t cplm=CPLM_ONLY)
bool SendPMT(const cCiCaPmt &CaPmt)
cCiConditionalAccessSupport(int SessionId, cCiTransportConnection *Tc)
dvbca_vector m_caSystemIds
dvbca_vector GetCaSystemIds(void)
bool Process(int Length=0, const uint8_t *Data=nullptr) override
bool NeedCaPmt(void) const
bool Process(int Length=0, const uint8_t *Data=nullptr) override
cCiDateTime(int SessionId, cCiTransportConnection *Tc)
void SetTimeOffset(double offset)
bool Reply(const char *s)
virtual int NumSlots(void)=0
static cCiHandler * CreateCiHandler(const char *FileName)
cCiMMI(int SessionId, cCiTransportConnection *Tc)
bool HasUserIO(void) override
bool Process(int Length=0, const uint8_t *Data=nullptr) override
bool SendMenuAnswer(uint8_t Selection)
bool SendAnswer(const char *Text)
cCiEnquiry * Enquiry(void)
std::string GetText(int &Length, const uint8_t **Data)
bool Process(int Length=0, const uint8_t *Data=nullptr) override
cCiResourceManager(int SessionId, cCiTransportConnection *Tc)
static int GetTag(int &Length, const uint8_t **Data)
const cCiTransportConnection * Tc(void)
cCiTransportConnection * m_tc
virtual bool Process(int Length=0, const uint8_t *Data=nullptr)
int ResourceId(void) const
int SendData(int Tag, std::vector< uint8_t > &Data)
int SendData(int Tag, int Length=0, const uint8_t *Data=nullptr)
static const uint8_t * GetData(const uint8_t *Data, int &Length)
virtual bool HasUserIO(void)
virtual ~cCiSession()=default
int SessionId(void) const
cCiSession(int SessionId, int ResourceId, cCiTransportConnection *Tc)
~cCiTransportConnection()
int CreateConnection(void)
void Init(int Fd, uint8_t Slot, uint8_t Tcid)
int SendData(std::vector< uint8_t > &Data)
bool DataAvailable(void) const
std::chrono::milliseconds m_lastPoll
int LastResponse(void) const
int SendTPDU(uint8_t Tag, int Length=0, const uint8_t *Data=nullptr) const
cCiTransportConnection(void)
int SendData(int Length, const uint8_t *Data)
const uint8_t * Data(int &Length)
std::array< cCiTransportConnection, MAX_CI_CONNECT > m_tc
bool ResetSlot(int Slot) const
cCiTransportConnection * NewConnection(int Slot)
cCiTransportLayer(int Fd, int NumSlots)
cCiTransportConnection * Process(int Slot)
bool ModuleReady(int Slot) const
bool NeedCaPmt(void) override
int SendData(unsigned tag, struct ca_msg *msg)
bool EnterMenu(int Slot) override
cHlCiHandler(int Fd, int NumSlots)
cCiMenu * GetMenu(void) override
cCiEnquiry * GetEnquiry(void) override
dvbca_vector GetCaSystemIds(int Slot) override
bool SetCaPmt(cCiCaPmt &CaPmt)
dvbca_vector m_caSystemIds
bool Process(void) override
int GetData(unsigned tag, struct ca_msg *msg)
int CommHL(unsigned tag, unsigned function, struct ca_msg *msg) const
bool Reset(int Slot) const
bool CloseSession(int SessionId)
bool OpenSession(int Length, const uint8_t *Data)
int CloseAllSessions(int Slot)
bool Send(uint8_t Tag, int SessionId, int ResourceId=0, int Status=-1)
cCiMenu * GetMenu(void) override
cCiSession * m_sessions[MAX_CI_SESSION]
cCiEnquiry * GetEnquiry(void) override
bool Process(void) override
int NumSlots(void) override
cCiTransportLayer * m_tpl
void SetTimeOffset(double offset_in_seconds) override
cLlCiHandler(int Fd, int NumSlots)
cCiSession * CreateSession(int ResourceId)
bool EnterMenu(int Slot) override
static int ResourceIdToInt(const uint8_t *Data)
cCiSession * GetSessionByResourceId(int ResourceId, int Slot)
dvbca_vector GetCaSystemIds(int Slot) override
cCiSession * GetSessionBySessionId(int SessionId)
bool SetCaPmt(cCiCaPmt &CaPmt)
cCiTransportConnection * m_tc
const uint8_t * Data(int &Length)
std::array< uint8_t, MAX_TPDU_SIZE > m_data
const uint8_t * GetData(const uint8_t *Data, int &Length) const
static constexpr size_t MAX_TPDU_SIZE
@ AOT_SUBTITLE_DOWNLOAD_LAST
@ AOT_APPLICATION_INFO_ENQ
@ AOT_CONNECTION_DESCRIPTOR
@ AOT_SUBTITLE_SEGMENT_MORE
@ AOT_SUBTITLE_DOWNLOAD_MORE
@ AOT_SUBTITLE_SEGMENT_LAST
static constexpr uint8_t DATA_INDICATOR
static constexpr int SIZE_INDICATOR
static constexpr uint8_t BYTE1(uint16_t a)
static uint8_t * SetLength(uint8_t *Data, int Length)
static constexpr size_t MAX_CI_CONNECT
@ DCC_DISPLAY_CHARACTER_TABLE_LIST
@ DCC_OVERLAY_GRAPHICS_CHARACTERISTICS
@ DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS
@ DCC_INPUT_CHARACTER_TABLE_LIST
@ RI_APPLICATION_INFORMATION
@ RI_CONDITIONAL_ACCESS_SUPPORT
static constexpr uint8_t EF_BLIND
static constexpr int MAX_TPDU_DATA
@ DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS
@ DRI_UNKNOWN_DISPLAY_CONTROL_CMD
@ DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS
@ DRI_UNKNOWN_CHARACTER_TABLE
@ DRI_LIST_DISPLAY_CHARACTER_TABLES
@ DRI_LIST_INPUT_CHARACTER_TABLES
static ssize_t safe_read(int filedes, void *buffer, size_t size)
static constexpr int TIMEOUT
static std::string GetString(int &Length, const uint8_t **Data)
static bool sDebugProtocol
static constexpr int CAM_READ_TIMEOUT
static std::string CopyString(int Length, const uint8_t *Data)
@ MM_LOW_LEVEL_OVERLAY_GRAPHICS
@ MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS
static constexpr uint8_t DEC2BCD(uint8_t d)
#define dbgprotocol(a...)
static constexpr std::chrono::milliseconds POLL_INTERVAL
static constexpr int ERROR
static constexpr int8_t MAX_CONNECT_RETRIES
@ ST_CLOSE_SESSION_RESPONSE
@ ST_CLOSE_SESSION_REQUEST
@ ST_OPEN_SESSION_RESPONSE
@ ST_OPEN_SESSION_REQUEST
@ ST_CREATE_SESSION_RESPONSE
static const uint8_t * GetLength(const uint8_t *Data, int &Length)
static bool sDumpTPDUDataTransfer
static constexpr uint8_t BYTE0(uint16_t a)
static constexpr time_t WRKRND_TIME_BEFORE_ENTER_MENU
std::vector< uint16_t > dvbca_vector
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
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
def read(device=None, features=[])
def write(text, progress=True)