vdr 2.8.3
ci.c
Go to the documentation of this file.
1/*
2 * ci.c: Common Interface
3 *
4 * See the main source file 'vdr.c' for copyright information and
5 * how to reach the author.
6 *
7 * $Id: ci.c 5.3 2026/05/30 12:27:12 kls Exp $
8 */
9
10#include "ci.h"
11#include <ctype.h>
12#include <linux/dvb/ca.h>
13#include <malloc.h>
14#include <netinet/in.h>
15#include <poll.h>
16#include <stdio.h>
17#include <string.h>
18#include <sys/ioctl.h>
19#include <time.h>
20#include <unistd.h>
21#include "device.h"
22#include "mtd.h"
23#include "pat.h"
24#include "receiver.h"
25#include "remux.h"
26#include "libsi/si.h"
27#include "skins.h"
28#include "tools.h"
29
30// Set these to 'true' for debug output:
31static bool DumpTPDUDataTransfer = false;
32static bool DebugProtocol = false;
33static bool DumpPolls = false;
34static bool DumpDateTime = false;
35
36#define dbgprotocol(a...) if (DebugProtocol) fprintf(stderr, a)
37
38// --- Helper functions ------------------------------------------------------
39
40#define SIZE_INDICATOR 0x80
41
42static const uint8_t *GetLength(const uint8_t *Data, int &Length)
46{
47 Length = *Data++;
48 if ((Length & SIZE_INDICATOR) != 0) {
49 int l = Length & ~SIZE_INDICATOR;
50 Length = 0;
51 for (int i = 0; i < l; i++)
52 Length = (Length << 8) | *Data++;
53 }
54 return Data;
55}
56
57static uint8_t *SetLength(uint8_t *Data, int Length)
60{
61 uint8_t *p = Data;
62 if (Length < 128)
63 *p++ = Length;
64 else {
65 int n = sizeof(Length);
66 for (int i = n - 1; i >= 0; i--) {
67 int b = (Length >> (8 * i)) & 0xFF;
68 if (p != Data || b)
69 *++p = b;
70 }
71 *Data = (p - Data) | SIZE_INDICATOR;
72 p++;
73 }
74 return p;
75}
76
77static char *CopyString(int Length, const uint8_t *Data)
80{
81 char *s = MALLOC(char, Length + 1);
82 char *p = s;
83 while (Length > 0) {
84 int c = *Data;
85 if (isprint(c)) // some CAMs send funny characters in their strings, let's just skip them
86 *p++ = c;
87 else if (c == 0x8A) // the character 0x8A is used as newline, so let's put a real '\n' in there
88 *p++ = '\n';
89 Length--;
90 Data++;
91 }
92 *p = 0;
93 return s;
94}
95
96static char *GetString(int &Length, const uint8_t **Data)
100{
101 if (Length > 0 && Data && *Data) {
102 int l = 0;
103 const uint8_t *d = GetLength(*Data, l);
104 char *s = CopyString(l, d);
105 Length -= d - *Data + l;
106 *Data = d + l;
107 return s;
108 }
109 return NULL;
110}
111
112// --- cCaPidReceiver --------------------------------------------------------
113
114// A receiver that is used to make the device receive the ECM pids, as well as the
115// CAT and the EMM pids.
116
117class cCaPidReceiver : public cReceiver {
118private:
121 uchar buffer[1024]; // CAT table length: 10 bit -> max. 1021 + 3 bytes
123 #define CAT_MAXPACKETS 6 // 6 * 184 = 1104 bytes for CAT table
124 uchar mtdCatBuffer[CAT_MAXPACKETS][TS_SIZE]; // TODO: handle multi table CATs!
129 void AddEmmPid(int Pid);
130 void DelEmmPids(void);
131public:
132 cCaPidReceiver(void);
133 virtual ~cCaPidReceiver() override { Detach(); }
134 virtual void Receive(const uchar *Data, int Length) override;
135 bool HasCaPids(void) const { return NumPids() - emmPids.Size() - 1 > 0; }
136 void Reset(void) { DelEmmPids(); catVersion = -1; }
137 bool HandlingPid(void);
146 };
147
149{
150 catVersion = -1;
151 bufp = NULL;
153 length = 0;
154 handlingPid = false;
155 cMutexLock MutexLock(&mutex);
156 handlingPid = true;
157 AddPid(CATPID);
158 handlingPid = false;
159}
160
162{
163 for (int i = 0; i < emmPids.Size(); i++) {
164 if (emmPids[i] == Pid)
165 return;
166 }
167 emmPids.Append(Pid);
168 cMutexLock MutexLock(&mutex);
169 handlingPid = true;
170 AddPid(Pid);
171 handlingPid = false;
172}
173
175{
176 cMutexLock MutexLock(&mutex);
177 handlingPid = true;
178 for (int i = 0; i < emmPids.Size(); i++)
179 DelPid(emmPids[i]);
180 emmPids.Clear();
181 handlingPid = false;
182}
183
184void cCaPidReceiver::Receive(const uchar *Data, int Length)
185{
186 if (TsPid(Data) == CATPID) {
187 cMtdCamSlot *MtdCamSlot = dynamic_cast<cMtdCamSlot *>(Device()->CamSlot());
188 const uchar *p = NULL;
189 if (TsPayloadStart(Data)) {
190 if (Data[5] == SI::TableIdCAT) {
191 if (bufp) { // incomplete multi-packet CAT
192 catVersion = -1;
193 bufp = NULL;
194 }
195 length = (int(Data[6] & 0x0F) << 8) | Data[7]; // section length (12 bit field)
196 if (length > 5) {
197 int v = (Data[10] & 0x3E) >> 1; // version number
198 if (v != catVersion) {
199 if (Data[11] == 0 && Data[12] == 0) { // section number, last section number
200 length += 3; // with TableIdCAT -> Data[5]
201 if (length > TS_SIZE - 5) {
202 int n = TS_SIZE - 5;
203 memcpy(buffer, Data + 5, n);
204 bufp = buffer + n;
205 length -= n;
206 }
207 else {
208 p = Data + 5; // no need to copy the data
209 }
210 if (MtdCamSlot) {
212 memcpy(mtdCatBuffer[mtdNumCatPackets++], Data, TS_SIZE);
213 }
214 }
215 else
216 dsyslog("multi table CAT section - unhandled!");
217 catVersion = v;
218 }
219 else if (MtdCamSlot) {
220 for (int i = 0; i < mtdNumCatPackets; i++)
221 MtdCamSlot->PutCat(mtdCatBuffer[i], TS_SIZE);
222 }
223 }
224 }
225 }
226 else if (bufp && length > 0) {
227 int n = min(length, TS_SIZE - 4);
228 if (bufp + n - buffer <= int(sizeof(buffer))) {
229 memcpy(bufp, Data + 4, n);
230 bufp += n;
231 length -= n;
232 if (length <= 0) {
233 p = buffer;
234 length = bufp - buffer;
235 }
236 if (MtdCamSlot)
237 memcpy(mtdCatBuffer[mtdNumCatPackets++], Data, TS_SIZE);
238 }
239 else {
240 esyslog("ERROR: buffer overflow in cCaPidReceiver::Receive()");
241 bufp = NULL;
242 length = 0;
243 }
244 }
245 if (p) {
246 if (!SI::CRC32::crc32((const char *)p, length, 0xFFFFFFFF)) { // <TableIdCAT,....,crc32>
247 DelEmmPids();
248 for (int i = 8; i < length - 4; i++) { // -4 = checksum
249 if (p[i] == 0x09) {
250 int CaId = int(p[i + 2] << 8) | p[i + 3];
251 int EmmPid = Peek13(p + i + 4);
252 AddEmmPid(EmmPid);
253 if (MtdCamSlot)
254 MtdMapPid(const_cast<uchar *>(p + i + 4), MtdCamSlot->MtdMapper());
255 switch (CaId >> 8) {
256 case 0x01: for (int j = i + 7; j < i + p[i + 1] + 2; j += 4) {
257 EmmPid = Peek13(p + j);
258 AddEmmPid(EmmPid);
259 if (MtdCamSlot)
260 MtdMapPid(const_cast<uchar *>(p + j), MtdCamSlot->MtdMapper());
261 }
262 break;
263 }
264 i += p[i + 1] + 2 - 1; // -1 to compensate for the loop increment
265 }
266 }
267 if (MtdCamSlot) {
268 // update crc32
269 uint32_t crc = SI::CRC32::crc32((const char *)p, length - 4, 0xFFFFFFFF); // <TableIdCAT....>[crc32]
270 uchar *c = const_cast<uchar *>(p + length - 4);
271 *c++ = crc >> 24;
272 *c++ = crc >> 16;
273 *c++ = crc >> 8;
274 *c++ = crc;
275 // modify CAT packets
276 const uchar *t = p;
277 for (int i = 0, j = 5; i < mtdNumCatPackets; i++, j = 4) {
278 int n = min(length, TS_SIZE - j);
279 memcpy(mtdCatBuffer[i] + j, t, n);
280 t += n;
281 length -= n;
282 MtdCamSlot->PutCat(mtdCatBuffer[i], TS_SIZE);
283 }
284 }
285 }
286 else {
287 esyslog("ERROR: wrong checksum in CAT");
288 catVersion = -1;
289 }
290 p = NULL;
291 bufp = NULL;
292 length = 0;
293 }
294 }
295}
296
298{
299 cMutexLock MutexLock(&mutex);
300 return handlingPid;
301}
302
303// --- cCaActivationReceiver -------------------------------------------------
304
305// A receiver that is used to make the device stay on a given channel and
306// keep the CAM slot assigned.
307
308#define UNSCRAMBLE_TIME 5 // seconds of receiving purely unscrambled data before considering the smart card "activated"
309#define TS_PACKET_FACTOR 1024 // only process every TS_PACKET_FACTORth packet to keep the load down
310
312private:
316protected:
317 virtual void Receive(const uchar *Data, int Length) override;
318public:
319 cCaActivationReceiver(const cChannel *Channel, cCamSlot *CamSlot);
320 virtual ~cCaActivationReceiver() override;
321 };
322
324:cReceiver(Channel, MINPRIORITY + 1)
325{
326 camSlot = CamSlot;
327 lastScrambledTime = time(NULL);
328 numTsPackets = 0;
329}
330
335
336void cCaActivationReceiver::Receive(const uchar *Data, int Length)
337{
338 if (numTsPackets++ % TS_PACKET_FACTOR == 0) {
339 time_t Now = time(NULL);
340 if (TsIsScrambled(Data))
341 lastScrambledTime = Now;
342 else if (Now - lastScrambledTime > UNSCRAMBLE_TIME) {
343 dsyslog("CAM %d: activated!", camSlot->MasterSlotNumber());
344 Skins.QueueMessage(mtInfo, tr("CAM activated!"));
345 cDevice *d = Device();
346 Detach();
347 if (d) {
348 if (cCamSlot *s = d->CamSlot())
349 s->CancelActivation(); // this will delete *this* object, so no more code referencing *this* after this call!
350 }
351 }
352 }
353}
354
355// --- cCamResponse ----------------------------------------------------------
356
357// CAM Response Actions:
358
359#define CRA_NONE 0
360#define CRA_DISCARD -1
361#define CRA_CONFIRM -2
362#define CRA_SELECT -3
363
364class cCamResponse : public cListObject {
365private:
367 char *text;
369public:
370 cCamResponse(void);
372 bool Parse(const char *s);
373 int Matches(int CamNumber, const char *Text) const;
374 };
375
377{
378 camNumber = -1;
379 text = NULL;
381}
382
384{
385 free(text);
386}
387
388bool cCamResponse::Parse(const char *s)
389{
390 // Number:
391 s = skipspace(s);
392 if (*s == '*') {
393 camNumber = 0; // all CAMs
394 s++;
395 }
396 else {
397 char *e;
398 camNumber = strtol(s, &e, 10);
399 if (e == s || camNumber <= 0)
400 return false;
401 s = e;
402 }
403 // Text:
404 s = skipspace(s);
405 char *t = const_cast<char *>(s); // might have to modify it
406 char *q = NULL; // holds a copy in case of backslashes
407 bool InQuotes = false;
408 while (*t) {
409 if (*t == '"') {
410 if (t == s) { // opening quotes
411 InQuotes = true;
412 s++;
413 }
414 else if (InQuotes) // closing quotes
415 break;
416 }
417 else if (*t == '\\') {
418 if (!q) { // need to make a copy in order to strip backslashes
419 q = strdup(s);
420 t = q + (t - s);
421 s = q;
422 }
423 memmove(t, t + 1, strlen(t));
424 }
425 else if (*t == ' ') {
426 if (!InQuotes)
427 break;
428 }
429 t++;
430 }
431 free(text); // just for safety
432 text = NULL;
433 if (t != s) {
434 text = strndup(s, t - s);
435 s = t + 1;
436 }
437 free(q);
438 if (!text)
439 return false;
440 // Action:
441 s = skipspace(s);
442 if (strcasecmp(s, "DISCARD") == 0) action = CRA_DISCARD;
443 else if (strcasecmp(s, "CONFIRM") == 0) action = CRA_CONFIRM;
444 else if (strcasecmp(s, "SELECT") == 0) action = CRA_SELECT;
445 else if (isnumber(s)) action = atoi(s);
446 else
447 return false;
448 return true;
449}
450
451int cCamResponse::Matches(int CamNumber, const char *Text) const
452{
453 if (!camNumber || camNumber == CamNumber) {
454 if (strcmp(text, Text) == 0)
455 return action;
456 }
457 return CRA_NONE;
458}
459
460// --- cCamResponses --------------------------------------------------------
461
462class cCamResponses : public cConfig<cCamResponse> {
463public:
464 int GetMatch(int CamNumber, const char *Text) const;
465 };
466
467int cCamResponses::GetMatch(int CamNumber, const char *Text) const
468{
469 for (const cCamResponse *cr = First(); cr; cr = Next(cr)) {
470 int Action = cr->Matches(CamNumber, Text);
471 if (Action != CRA_NONE) {
472 dsyslog("CAM %d: auto response %4d to '%s'\n", CamNumber, Action, Text);
473 return Action;
474 }
475 }
476 return CRA_NONE;
477}
478
480
481bool CamResponsesLoad(const char *FileName, bool AllowComments, bool MustExist)
482{
483 return CamResponses.Load(FileName, AllowComments, MustExist);
484}
485
486// --- cTPDU -----------------------------------------------------------------
487
488#define MAX_TPDU_SIZE 4096
489#define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4)
490
491#define DATA_INDICATOR 0x80
492
493#define T_SB 0x80
494#define T_RCV 0x81
495#define T_CREATE_TC 0x82
496#define T_CTC_REPLY 0x83
497#define T_DELETE_TC 0x84
498#define T_DTC_REPLY 0x85
499#define T_REQUEST_TC 0x86
500#define T_NEW_TC 0x87
501#define T_TC_ERROR 0x88
502#define T_DATA_LAST 0xA0
503#define T_DATA_MORE 0xA1
504
505class cTPDU {
506private:
507 int size;
509 const uint8_t *GetData(const uint8_t *Data, int &Length);
510public:
511 cTPDU(void) { size = 0; }
512 cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length = 0, const uint8_t *Data = NULL);
513 uint8_t Slot(void) { return buffer[0]; }
514 uint8_t Tcid(void) { return buffer[1]; }
515 uint8_t Tag(void) { return buffer[2]; }
516 const uint8_t *Data(int &Length) { return GetData(buffer + 3, Length); }
517 uint8_t Status(void);
518 uint8_t *Buffer(void) { return buffer; }
519 int Size(void) { return size; }
520 void SetSize(int Size) { size = Size; }
521 int MaxSize(void) { return sizeof(buffer); }
522 void Dump(int SlotNumber, bool Outgoing);
523 };
524
525cTPDU::cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length, const uint8_t *Data)
526{
527 size = 0;
528 buffer[0] = Slot;
529 buffer[1] = Tcid;
530 buffer[2] = Tag;
531 switch (Tag) {
532 case T_RCV:
533 case T_CREATE_TC:
534 case T_CTC_REPLY:
535 case T_DELETE_TC:
536 case T_DTC_REPLY:
537 case T_REQUEST_TC:
538 buffer[3] = 1; // length
539 buffer[4] = Tcid;
540 size = 5;
541 break;
542 case T_NEW_TC:
543 case T_TC_ERROR:
544 if (Length == 1) {
545 buffer[3] = 2; // length
546 buffer[4] = Tcid;
547 buffer[5] = Data[0];
548 size = 6;
549 }
550 else
551 esyslog("ERROR: invalid data length for TPDU tag 0x%02X: %d (%d/%d)", Tag, Length, Slot, Tcid);
552 break;
553 case T_DATA_LAST:
554 case T_DATA_MORE:
555 if (Length <= MAX_TPDU_DATA) {
556 uint8_t *p = buffer + 3;
557 p = SetLength(p, Length + 1);
558 *p++ = Tcid;
559 if (Length)
560 memcpy(p, Data, Length);
561 size = Length + (p - buffer);
562 }
563 else
564 esyslog("ERROR: invalid data length for TPDU tag 0x%02X: %d (%d/%d)", Tag, Length, Slot, Tcid);
565 break;
566 default:
567 esyslog("ERROR: unknown TPDU tag: 0x%02X (%d/%d)", Tag, Slot, Tcid);
568 }
569 }
570
571void cTPDU::Dump(int SlotNumber, bool Outgoing)
572{
573 if (DumpTPDUDataTransfer && (DumpPolls || Tag() != T_SB)) {
574#define MAX_DUMP 256
575 fprintf(stderr, " %d: %s ", SlotNumber, Outgoing ? "-->" : "<--");
576 for (int i = 0; i < size && i < MAX_DUMP; i++)
577 fprintf(stderr, "%02X ", buffer[i]);
578 fprintf(stderr, "%s\n", size >= MAX_DUMP ? "..." : "");
579 if (!Outgoing) {
580 fprintf(stderr, " ");
581 for (int i = 0; i < size && i < MAX_DUMP; i++)
582 fprintf(stderr, "%2c ", isprint(buffer[i]) ? buffer[i] : '.');
583 fprintf(stderr, "%s\n", size >= MAX_DUMP ? "..." : "");
584 }
585 }
586}
587
588const uint8_t *cTPDU::GetData(const uint8_t *Data, int &Length)
589{
590 if (size) {
591 Data = GetLength(Data, Length);
592 if (Length) {
593 Length--; // the first byte is always the tcid
594 return Data + 1;
595 }
596 }
597 return NULL;
598}
599
600uint8_t cTPDU::Status(void)
601{
602 if (size >= 4 && buffer[size - 4] == T_SB && buffer[size - 3] == 2)
603 return buffer[size - 1];
604 return 0;
605}
606
607// --- cCiTransportConnection ------------------------------------------------
608
609#define MAX_SESSIONS_PER_TC 16
610
612private:
616 uint8_t tcid;
623 cCiSession *sessions[MAX_SESSIONS_PER_TC + 1]; // session numbering starts with 1
625 void SendTPDU(uint8_t Tag, int Length = 0, const uint8_t *Data = NULL);
626 void SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId = 0, int Status = -1);
627 void Poll(void);
628 uint32_t ResourceIdToInt(const uint8_t *Data);
629 cCiSession *GetSessionBySessionId(uint16_t SessionId);
630 void OpenSession(int Length, const uint8_t *Data);
631 void CloseSession(uint16_t SessionId);
632 void HandleSessions(cTPDU *TPDU);
633public:
635 virtual ~cCiTransportConnection();
636 void SetTsPostProcessor(cCiSession *CiSession);
637 bool TsPostProcess(uint8_t *TsPacket);
638 cCamSlot *CamSlot(void) { return camSlot; }
639 uint8_t Tcid(void) const { return tcid; }
642 const char *GetCamName(void);
643 bool Ready(void);
644 bool HasUserIO(void) { return hasUserIO; }
645 void SendData(int Length, const uint8_t *Data);
646 bool Process(cTPDU *TPDU = NULL);
647 cCiSession *GetSessionByResourceId(uint32_t ResourceId);
648 };
649
650// --- cCiSession ------------------------------------------------------------
651
652// Session Tags:
653
654#define ST_SESSION_NUMBER 0x90
655#define ST_OPEN_SESSION_REQUEST 0x91
656#define ST_OPEN_SESSION_RESPONSE 0x92
657#define ST_CREATE_SESSION 0x93
658#define ST_CREATE_SESSION_RESPONSE 0x94
659#define ST_CLOSE_SESSION_REQUEST 0x95
660#define ST_CLOSE_SESSION_RESPONSE 0x96
661
662// Session Status:
663
664#define SS_OK 0x00
665#define SS_NOT_ALLOCATED 0xF0
666
667// Resource Identifiers:
668
669#define RI_RESOURCE_MANAGER 0x00010041
670#define RI_APPLICATION_INFORMATION 0x00020041
671#define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041
672#define RI_HOST_CONTROL 0x00200041
673#define RI_DATE_TIME 0x00240041
674#define RI_MMI 0x00400041
675
676// Application Object Tags:
677
678#define AOT_NONE 0x000000
679#define AOT_PROFILE_ENQ 0x9F8010
680#define AOT_PROFILE 0x9F8011
681#define AOT_PROFILE_CHANGE 0x9F8012
682#define AOT_APPLICATION_INFO_ENQ 0x9F8020
683#define AOT_APPLICATION_INFO 0x9F8021
684#define AOT_ENTER_MENU 0x9F8022
685#define AOT_CA_INFO_ENQ 0x9F8030
686#define AOT_CA_INFO 0x9F8031
687#define AOT_CA_PMT 0x9F8032
688#define AOT_CA_PMT_REPLY 0x9F8033
689#define AOT_TUNE 0x9F8400
690#define AOT_REPLACE 0x9F8401
691#define AOT_CLEAR_REPLACE 0x9F8402
692#define AOT_ASK_RELEASE 0x9F8403
693#define AOT_DATE_TIME_ENQ 0x9F8440
694#define AOT_DATE_TIME 0x9F8441
695#define AOT_CLOSE_MMI 0x9F8800
696#define AOT_DISPLAY_CONTROL 0x9F8801
697#define AOT_DISPLAY_REPLY 0x9F8802
698#define AOT_TEXT_LAST 0x9F8803
699#define AOT_TEXT_MORE 0x9F8804
700#define AOT_KEYPAD_CONTROL 0x9F8805
701#define AOT_KEYPRESS 0x9F8806
702#define AOT_ENQ 0x9F8807
703#define AOT_ANSW 0x9F8808
704#define AOT_MENU_LAST 0x9F8809
705#define AOT_MENU_MORE 0x9F880A
706#define AOT_MENU_ANSW 0x9F880B
707#define AOT_LIST_LAST 0x9F880C
708#define AOT_LIST_MORE 0x9F880D
709#define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E
710#define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F
711#define AOT_DISPLAY_MESSAGE 0x9F8810
712#define AOT_SCENE_END_MARK 0x9F8811
713#define AOT_SCENE_DONE 0x9F8812
714#define AOT_SCENE_CONTROL 0x9F8813
715#define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814
716#define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815
717#define AOT_FLUSH_DOWNLOAD 0x9F8816
718#define AOT_DOWNLOAD_REPLY 0x9F8817
719#define AOT_COMMS_CMD 0x9F8C00
720#define AOT_CONNECTION_DESCRIPTOR 0x9F8C01
721#define AOT_COMMS_REPLY 0x9F8C02
722#define AOT_COMMS_SEND_LAST 0x9F8C03
723#define AOT_COMMS_SEND_MORE 0x9F8C04
724#define AOT_COMMS_RCV_LAST 0x9F8C05
725#define AOT_COMMS_RCV_MORE 0x9F8C06
726
727#define RESOURCE_CLASS_MASK 0xFFFF0000
728
735
739
741{
742 resourceId = Id;
743}
744
746{
747 tc->SetTsPostProcessor(this);
748}
749
750int cCiSession::GetTag(int &Length, const uint8_t **Data)
754{
755 if (Length >= 3 && Data && *Data) {
756 int t = 0;
757 for (int i = 0; i < 3; i++)
758 t = (t << 8) | *(*Data)++;
759 Length -= 3;
760 return t;
761 }
762 return AOT_NONE;
763}
764
765const uint8_t *cCiSession::GetData(const uint8_t *Data, int &Length)
766{
767 Data = GetLength(Data, Length);
768 return Length ? Data : NULL;
769}
770
771void cCiSession::SendData(int Tag, int Length, const uint8_t *Data)
772{
773 uint8_t buffer[MAX_TPDU_SIZE];
774 uint8_t *p = buffer;
775 *p++ = ST_SESSION_NUMBER;
776 *p++ = 0x02;
777 *p++ = (sessionId >> 8) & 0xFF;
778 *p++ = sessionId & 0xFF;
779 *p++ = (Tag >> 16) & 0xFF;
780 *p++ = (Tag >> 8) & 0xFF;
781 *p++ = Tag & 0xFF;
782 p = SetLength(p, Length);
783 if (p - buffer + Length < int(sizeof(buffer))) {
784 if (Data)
785 memcpy(p, Data, Length);
786 p += Length;
787 tc->SendData(p - buffer, buffer);
788 }
789 else
790 esyslog("ERROR: CAM %d: data length (%d) exceeds buffer size", CamSlot()->SlotNumber(), Length);
791}
792
794{
795 return Tc()->CamSlot();
796}
797
798void cCiSession::Process(int Length, const uint8_t *Data)
799{
800}
801
802// --- cCiResourceManager ----------------------------------------------------
803
805private:
806 int state;
807public:
809 virtual void Process(int Length = 0, const uint8_t *Data = NULL) override;
810 };
811
814{
815 dbgprotocol("Slot %d: new Resource Manager (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
816 state = 0;
817}
818
819void cCiResourceManager::Process(int Length, const uint8_t *Data)
820{
821 if (Data) {
822 int Tag = GetTag(Length, &Data);
823 switch (Tag) {
824 case AOT_PROFILE_ENQ: {
825 dbgprotocol("Slot %d: <== Profile Enquiry (%d)\n", CamSlot()->SlotNumber(), SessionId());
826 dbgprotocol("Slot %d: ==> Profile (%d)\n", CamSlot()->SlotNumber(), SessionId());
827 SendData(AOT_PROFILE, CiResourceHandlers.NumIds() * sizeof(uint32_t), (uint8_t*)CiResourceHandlers.Ids());
828 state = 3;
829 }
830 break;
831 case AOT_PROFILE: {
832 dbgprotocol("Slot %d: <== Profile (%d)\n", CamSlot()->SlotNumber(), SessionId());
833 if (state == 1) {
834 int l = 0;
835 const uint8_t *d = GetData(Data, l);
836 if (l > 0 && d)
837 esyslog("ERROR: CAM %d: resource manager: unexpected data", CamSlot()->SlotNumber());
838 dbgprotocol("Slot %d: ==> Profile Change (%d)\n", CamSlot()->SlotNumber(), SessionId());
840 state = 2;
841 }
842 else {
843 esyslog("ERROR: CAM %d: resource manager: unexpected tag %06X in state %d", CamSlot()->SlotNumber(), Tag, state);
844 }
845 }
846 break;
847 default: esyslog("ERROR: CAM %d: resource manager: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
848 }
849 }
850 else if (state == 0) {
851 dbgprotocol("Slot %d: ==> Profile Enq (%d)\n", CamSlot()->SlotNumber(), SessionId());
853 state = 1;
854 }
855}
856
857// --- cCiApplicationInformation ---------------------------------------------
858
861{
862 dbgprotocol("Slot %d: new Application Information (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
863 state = 0;
864 menuString = NULL;
865}
866
871
872void cCiApplicationInformation::Process(int Length, const uint8_t *Data)
873{
874 if (Data) {
875 int Tag = GetTag(Length, &Data);
876 switch (Tag) {
878 dbgprotocol("Slot %d: <== Application Info (%d)\n", CamSlot()->SlotNumber(), SessionId());
879 int l = 0;
880 const uint8_t *d = GetData(Data, l);
881 if ((l -= 1) < 0) break;
882 applicationType = *d++;
883 if ((l -= 2) < 0) break;
884 applicationManufacturer = ntohs(get_unaligned((uint16_t *)d));
885 d += 2;
886 if ((l -= 2) < 0) break;
887 manufacturerCode = ntohs(get_unaligned((uint16_t *)d));
888 d += 2;
889 free(menuString);
890 menuString = GetString(l, &d);
891 isyslog("CAM %d: %s, %02X, %04X, %04X", CamSlot()->SlotNumber(), menuString, applicationType, applicationManufacturer, manufacturerCode);
892 state = 2;
893 }
894 break;
895 default: esyslog("ERROR: CAM %d: application information: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
896 }
897 }
898 else if (state == 0) {
899 dbgprotocol("Slot %d: ==> Application Info Enq (%d)\n", CamSlot()->SlotNumber(), SessionId());
901 state = 1;
902 }
903}
904
906{
907 if (state == 2) {
908 dbgprotocol("Slot %d: ==> Enter Menu (%d)\n", CamSlot()->SlotNumber(), SessionId());
910 return true;
911 }
912 return false;
913}
914
915// --- cCiCaPmt --------------------------------------------------------------
916
917#define MAXCASYSTEMIDS 64
918
919// Ca Pmt List Management:
920
921#define CPLM_MORE 0x00
922#define CPLM_FIRST 0x01
923#define CPLM_LAST 0x02
924#define CPLM_ONLY 0x03
925#define CPLM_ADD 0x04
926#define CPLM_UPDATE 0x05
927
928// Ca Pmt Cmd Ids:
929
930#define CPCI_OK_DESCRAMBLING 0x01
931#define CPCI_OK_MMI 0x02
932#define CPCI_QUERY 0x03
933#define CPCI_NOT_SELECTED 0x04
934
935class cCiCaPmt {
937private:
938 uint8_t cmdId;
945 int caSystemIds[MAXCASYSTEMIDS + 1]; // list is zero terminated!
946 void AddCaDescriptors(int Length, const uint8_t *Data);
947public:
948 cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds);
949 uint8_t CmdId(void) { return cmdId; }
950 void SetListManagement(uint8_t ListManagement);
951 uint8_t ListManagement(void) { return capmt.Get(0); }
952 void AddPid(int Pid, uint8_t StreamType);
953 void MtdMapPids(cMtdMapper *MtdMapper);
954 };
955
956cCiCaPmt::cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds)
957{
958 cmdId = CmdId;
959 source = Source;
960 transponder = Transponder;
961 programNumber = ProgramNumber;
962 int i = 0;
963 if (CaSystemIds) {
964 for (; CaSystemIds[i]; i++)
965 caSystemIds[i] = CaSystemIds[i];
966 }
967 caSystemIds[i] = 0;
970 capmt.Append((ProgramNumber >> 8) & 0xFF);
971 capmt.Append( ProgramNumber & 0xFF);
972 capmt.Append(0x01); // version_number, current_next_indicator - apparently vn doesn't matter, but cni must be 1
974 capmt.Append(0x00); // program_info_length H (at program level)
975 capmt.Append(0x00); // program_info_length L
977}
978
983
984void cCiCaPmt::AddPid(int Pid, uint8_t StreamType)
985{
986 if (Pid) {
988 capmt.Append(StreamType);
989 capmt.Append((Pid >> 8) & 0xFF);
990 capmt.Append( Pid & 0xFF);
992 capmt.Append(0x00); // ES_info_length H (at ES level)
993 capmt.Append(0x00); // ES_info_length L
995 }
996}
997
998void cCiCaPmt::AddCaDescriptors(int Length, const uint8_t *Data)
999{
1000 if (esInfoLengthPos) {
1001 if (Length || cmdId == CPCI_QUERY) {
1003 capmt.Append(Data, Length);
1004 int l = capmt.Length() - esInfoLengthPos - 2;
1005 capmt.Set(esInfoLengthPos, (l >> 8) & 0xFF);
1006 capmt.Set(esInfoLengthPos + 1, l & 0xFF);
1007 }
1008 esInfoLengthPos = 0;
1009 }
1010 else
1011 esyslog("ERROR: adding CA descriptor without Pid!");
1012}
1013
1014static int MtdMapCaDescriptor(uchar *p, cMtdMapper *MtdMapper)
1015{
1016 // See pat.c: cCaDescriptor::cCaDescriptor() for the layout of the data!
1017 if (*p == SI::CaDescriptorTag) {
1018 int l = *++p;
1019 if (l >= 4) {
1020 MtdMapPid(p + 3, MtdMapper);
1021 return l + 2;
1022 }
1023 else
1024 esyslog("ERROR: wrong length (%d) in MtdMapCaDescriptor()", l);
1025 }
1026 else
1027 esyslog("ERROR: wrong tag (%d) in MtdMapCaDescriptor()", *p);
1028 return -1;
1029}
1030
1031static int MtdMapCaDescriptors(uchar *p, cMtdMapper *MtdMapper)
1032{
1033 int Length = p[0] * 256 + p[1];
1034 if (Length >= 3) {
1035 p += 3;
1036 int m = Length - 1;
1037 while (m > 0) {
1038 int l = MtdMapCaDescriptor(p, MtdMapper);
1039 if (l > 0) {
1040 p += l;
1041 m -= l;
1042 }
1043 }
1044 }
1045 return Length + 2;
1046}
1047
1048static int MtdMapStream(uchar *p, cMtdMapper *MtdMapper)
1049{
1050 // See ci.c: cCiCaPmt::AddPid() for the layout of the data!
1051 MtdMapPid(p + 1, MtdMapper);
1052 int l = MtdMapCaDescriptors(p + 3, MtdMapper);
1053 if (l > 0)
1054 return l + 3;
1055 return -1;
1056}
1057
1058static int MtdMapStreams(uchar *p, cMtdMapper *MtdMapper, int Length)
1059{
1060 int m = Length;
1061 while (m >= 5) {
1062 int l = MtdMapStream(p, MtdMapper);
1063 if (l > 0) {
1064 p += l;
1065 m -= l;
1066 }
1067 else
1068 break;
1069 }
1070 return Length;
1071}
1072
1074{
1075 uchar *p = capmt.Data();
1076 int m = capmt.Length();
1077 if (m >= 3) {
1078 MtdMapSid(p + 1, MtdMapper);
1079 p += 4;
1080 m -= 4;
1081 if (m >= 2) {
1082 int l = MtdMapCaDescriptors(p, MtdMapper);
1083 if (l >= 0) {
1084 p += l;
1085 m -= l;
1086 MtdMapStreams(p, MtdMapper, m);
1087 }
1088 }
1089 }
1090}
1091
1092// --- cCiConditionalAccessSupport -------------------------------------------
1093
1094// CA Enable Ids:
1095
1096#define CAEI_POSSIBLE 0x01
1097#define CAEI_POSSIBLE_COND_PURCHASE 0x02
1098#define CAEI_POSSIBLE_COND_TECHNICAL 0x03
1099#define CAEI_NOT_POSSIBLE_ENTITLEMENT 0x71
1100#define CAEI_NOT_POSSIBLE_TECHNICAL 0x73
1101
1102#define CA_ENABLE_FLAG 0x80
1103
1104#define CA_ENABLE(x) (((x) & CA_ENABLE_FLAG) ? (x) & ~CA_ENABLE_FLAG : 0)
1105
1106#define QUERY_WAIT_TIME 500 // ms to wait before sending a query
1107#define QUERY_REPLY_TIMEOUT 2000 // ms to wait for a reply to a query
1108#define QUERY_RETRIES 6 // max. number of retries to check if there is a reply to a query
1109
1111private:
1114 int caSystemIds[MAXCASYSTEMIDS + 1]; // list is zero terminated!
1118public:
1120 virtual void Process(int Length = 0, const uint8_t *Data = NULL) override;
1121 const int *GetCaSystemIds(void) { return caSystemIds; }
1122 void SendPMT(cCiCaPmt *CaPmt);
1123 bool RepliesToQuery(void) { return repliesToQuery; }
1124 bool Ready(void) { return state >= 4; }
1125 bool ReceivedReply(void) { return state >= 5; }
1126 bool CanDecrypt(void) { return state == 6; }
1127 };
1128
1131{
1132 dbgprotocol("Slot %d: new Conditional Access Support (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
1133 state = 0; // inactive
1134 caSystemIds[numCaSystemIds = 0] = 0;
1135 repliesToQuery = false;
1136 numRetries = 0;
1137}
1138
1139void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
1140{
1141 if (Data) {
1142 int Tag = GetTag(Length, &Data);
1143 switch (Tag) {
1144 case AOT_CA_INFO: {
1145 dbgprotocol("Slot %d: <== Ca Info (%d)", CamSlot()->SlotNumber(), SessionId());
1146 cString Ids;
1147 numCaSystemIds = 0;
1148 int l = 0;
1149 const uint8_t *d = GetData(Data, l);
1150 while (l > 1) {
1151 uint16_t id = ((uint16_t)(*d) << 8) | *(d + 1);
1152 Ids = cString::sprintf("%s %04X", *Ids ? *Ids : "", id);
1153 dbgprotocol(" %04X", id);
1154 d += 2;
1155 l -= 2;
1158 else {
1159 esyslog("ERROR: CAM %d: too many CA system IDs!", CamSlot()->SlotNumber());
1160 break;
1161 }
1162 }
1164 dbgprotocol("\n");
1165 if (state == 1) {
1166 timer.Set(0);
1168 state = 2; // got ca info
1169 }
1170 dsyslog("CAM %d: system ids:%s", CamSlot()->SlotNumber(), *Ids ? *Ids : " none");
1171 }
1172 break;
1173 case AOT_CA_PMT_REPLY: {
1174 dbgprotocol("Slot %d: <== Ca Pmt Reply (%d)", CamSlot()->SlotNumber(), SessionId());
1175 if (!repliesToQuery) {
1176 if (CamSlot()->IsMasterSlot())
1177 dsyslog("CAM %d: replies to QUERY - multi channel decryption (MCD) possible", CamSlot()->SlotNumber());
1178 repliesToQuery = true;
1179 if (CamSlot()->MtdAvailable()) {
1180 if (CamSlot()->IsMasterSlot())
1181 dsyslog("CAM %d: supports multi transponder decryption (MTD)", CamSlot()->SlotNumber());
1182 CamSlot()->MtdActivate(true);
1183 }
1184 }
1185 state = 5; // got ca pmt reply
1186 int l = 0;
1187 const uint8_t *d = GetData(Data, l);
1188 if (l > 1) {
1189 uint16_t pnr = ((uint16_t)(*d) << 8) | *(d + 1);
1190 dbgprotocol(" %d", pnr);
1191 d += 2;
1192 l -= 2;
1193 if (l > 0) {
1194 dbgprotocol(" %02X", *d);
1195 d += 1;
1196 l -= 1;
1197 if (l > 0) {
1198 if (l % 3 == 0 && l > 1) {
1199 // The EN50221 standard defines that the next byte is supposed
1200 // to be the CA_enable value at programme level. However, there are
1201 // CAMs (for instance the AlphaCrypt with firmware <= 3.05) that
1202 // insert a two byte length field here.
1203 // This is a workaround to skip this length field:
1204 uint16_t len = ((uint16_t)(*d) << 8) | *(d + 1);
1205 if (len == l - 2) {
1206 d += 2;
1207 l -= 2;
1208 }
1209 }
1210 unsigned char caepl = *d;
1211 dbgprotocol(" %02X", caepl);
1212 d += 1;
1213 l -= 1;
1214 bool ok = true;
1215 if (l <= 2)
1216 ok = CA_ENABLE(caepl) == CAEI_POSSIBLE;
1217 while (l > 2) {
1218 uint16_t pid = ((uint16_t)(*d) << 8) | *(d + 1);
1219 unsigned char caees = *(d + 2);
1220 dbgprotocol(" %d=%02X", pid, caees);
1221 d += 3;
1222 l -= 3;
1223 if (CA_ENABLE(caees) != CAEI_POSSIBLE)
1224 ok = false;
1225 }
1226 if (ok)
1227 state = 6; // descrambling possible
1228 }
1229 }
1230 }
1231 dbgprotocol("\n");
1232 }
1233 break;
1234 default: esyslog("ERROR: CAM %d: conditional access support: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
1235 }
1236 }
1237 else if (state == 0) {
1238 dbgprotocol("Slot %d: ==> Ca Info Enq (%d)\n", CamSlot()->SlotNumber(), SessionId());
1240 state = 1; // enquired ca info
1241 }
1242 else if ((state == 2 || state == 3) && timer.TimedOut()) {
1243 if (numRetries-- > 0) {
1244 cCiCaPmt CaPmt(CPCI_QUERY, 0, 0, 0, NULL);
1245 SendPMT(&CaPmt);
1247 state = 3; // waiting for reply
1248 }
1249 else {
1250 dsyslog("CAM %d: doesn't reply to QUERY - only a single channel can be decrypted", CamSlot()->SlotNumber());
1251 CamSlot()->MtdActivate(false);
1252 state = 4; // normal operation
1253 }
1254 }
1255}
1256
1258{
1259 if (CaPmt && state >= 2) {
1260 dbgprotocol("Slot %d: ==> Ca Pmt (%d) %d %d\n", CamSlot()->SlotNumber(), SessionId(), CaPmt->ListManagement(), CaPmt->CmdId());
1261 SendData(AOT_CA_PMT, CaPmt->capmt.Length(), CaPmt->capmt.Data());
1262 state = 4; // sent ca pmt
1263 }
1264}
1265
1266// --- cCiHostControl --------------------------------------------------------
1267
1269public:
1271 virtual void Process(int Length = 0, const uint8_t *Data = NULL) override;
1272 };
1273
1276{
1277 dbgprotocol("Slot %d: new Host Control (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
1278}
1279
1280void cCiHostControl::Process(int Length, const uint8_t* Data)
1281{
1282 if (Data) {
1283 int Tag = GetTag(Length, &Data);
1284 switch (Tag) {
1285 case AOT_TUNE:
1286 dbgprotocol("Slot %d: <== Host Control Tune (%d)\n", CamSlot()->SlotNumber(), SessionId());
1287 break;
1288 case AOT_REPLACE:
1289 dbgprotocol("Slot %d: <== Host Control Replace (%d)\n", CamSlot()->SlotNumber(), SessionId());
1290 break;
1291 case AOT_CLEAR_REPLACE:
1292 dbgprotocol("Slot %d: <== Host Control Clear Replace (%d)\n", CamSlot()->SlotNumber(), SessionId());
1293 break;
1294 default: esyslog("ERROR: CAM %d: Host Control: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
1295 }
1296 }
1297}
1298
1299// --- cCiDateTime -----------------------------------------------------------
1300
1301class cCiDateTime : public cCiSession {
1302private:
1304 time_t lastTime;
1305 void SendDateTime(void);
1306public:
1308 virtual void Process(int Length = 0, const uint8_t *Data = NULL) override;
1309 };
1310
1313{
1314 interval = 0;
1315 lastTime = 0;
1316 dbgprotocol("Slot %d: new Date Time (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
1317}
1318
1320{
1321 time_t t = time(NULL);
1322 struct tm tm_gmt;
1323 struct tm tm_loc;
1324 if (gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc)) {
1325 int Y = tm_gmt.tm_year;
1326 int M = tm_gmt.tm_mon + 1;
1327 int D = tm_gmt.tm_mday;
1328 int L = (M == 1 || M == 2) ? 1 : 0;
1329 int MJD = 14956 + D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001);
1330#define DEC2BCD(d) uint8_t(((d / 10) << 4) + (d % 10))
1331#pragma pack(1)
1332 struct tTime { uint16_t mjd; uint8_t h, m, s; short offset; };
1333#pragma pack()
1334 tTime T = { mjd : htons(MJD), h : DEC2BCD(tm_gmt.tm_hour), m : DEC2BCD(tm_gmt.tm_min), s : DEC2BCD(tm_gmt.tm_sec), offset : short(htons(tm_loc.tm_gmtoff / 60)) };
1335 bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer;
1337 if (DumpDateTime)
1338 dbgprotocol("Slot %d: ==> Date Time (%d)\n", CamSlot()->SlotNumber(), SessionId());
1339 SendData(AOT_DATE_TIME, 7, (uint8_t*)&T);
1340 DumpTPDUDataTransfer = OldDumpTPDUDataTransfer;
1341 }
1342}
1343
1344void cCiDateTime::Process(int Length, const uint8_t *Data)
1345{
1346 if (Data) {
1347 int Tag = GetTag(Length, &Data);
1348 switch (Tag) {
1349 case AOT_DATE_TIME_ENQ: {
1350 interval = 0;
1351 int l = 0;
1352 const uint8_t *d = GetData(Data, l);
1353 if (l > 0)
1354 interval = *d;
1355 dbgprotocol("Slot %d: <== Date Time Enq (%d), interval = %d\n", CamSlot()->SlotNumber(), SessionId(), interval);
1356 lastTime = time(NULL);
1357 SendDateTime();
1358 }
1359 break;
1360 default: esyslog("ERROR: CAM %d: date time: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
1361 }
1362 }
1363 else if (interval && time(NULL) - lastTime > interval) {
1364 lastTime = time(NULL);
1365 SendDateTime();
1366 }
1367}
1368
1369// --- cCiMMI ----------------------------------------------------------------
1370
1371// Display Control Commands:
1372
1373#define DCC_SET_MMI_MODE 0x01
1374#define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
1375#define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
1376#define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
1377#define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
1378
1379// MMI Modes:
1380
1381#define MM_HIGH_LEVEL 0x01
1382#define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
1383#define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
1384
1385// Display Reply IDs:
1386
1387#define DRI_MMI_MODE_ACK 0x01
1388#define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
1389#define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
1390#define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
1391#define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
1392#define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
1393#define DRI_UNKNOWN_MMI_MODE 0xF1
1394#define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
1395
1396// Enquiry Flags:
1397
1398#define EF_BLIND 0x01
1399
1400// Answer IDs:
1401
1402#define AI_CANCEL 0x00
1403#define AI_ANSWER 0x01
1404
1405class cCiMMI : public cCiSession {
1406private:
1407 char *GetText(int &Length, const uint8_t **Data);
1410public:
1412 virtual ~cCiMMI() override;
1413 virtual void Process(int Length = 0, const uint8_t *Data = NULL) override;
1414 virtual bool HasUserIO(void) override { return menu || enquiry; }
1415 cCiMenu *Menu(bool Clear = false);
1416 cCiEnquiry *Enquiry(bool Clear = false);
1417 void SendMenuAnswer(uint8_t Selection);
1418 bool SendAnswer(const char *Text);
1419 bool SendCloseMMI(void);
1420 };
1421
1424{
1425 dbgprotocol("Slot %d: new MMI (session id %d)\n", CamSlot()->SlotNumber(), SessionId);
1426 menu = fetchedMenu = NULL;
1427 enquiry = fetchedEnquiry = NULL;
1428}
1429
1431{
1432 if (fetchedMenu) {
1433 cMutexLock MutexLock(fetchedMenu->mutex);
1434 fetchedMenu->mmi = NULL;
1435 }
1436 delete menu;
1437 if (fetchedEnquiry) {
1438 cMutexLock MutexLock(fetchedEnquiry->mutex);
1439 fetchedEnquiry->mmi = NULL;
1440 }
1441 delete enquiry;
1442}
1443
1444char *cCiMMI::GetText(int &Length, const uint8_t **Data)
1448{
1449 int Tag = GetTag(Length, Data);
1450 if (Tag == AOT_TEXT_LAST) {
1451 char *s = GetString(Length, Data);
1452 dbgprotocol("Slot %d: <== Text Last (%d) '%s'\n", CamSlot()->SlotNumber(), SessionId(), s);
1453 return s;
1454 }
1455 else
1456 esyslog("ERROR: CAM %d: MMI: unexpected text tag: %06X", CamSlot()->SlotNumber(), Tag);
1457 return NULL;
1458}
1459
1460void cCiMMI::Process(int Length, const uint8_t *Data)
1461{
1462 if (Data) {
1463 int Tag = GetTag(Length, &Data);
1464 switch (Tag) {
1465 case AOT_DISPLAY_CONTROL: {
1466 dbgprotocol("Slot %d: <== Display Control (%d)\n", CamSlot()->SlotNumber(), SessionId());
1467 int l = 0;
1468 const uint8_t *d = GetData(Data, l);
1469 if (l > 0) {
1470 switch (*d) {
1471 case DCC_SET_MMI_MODE:
1472 if (l == 2 && *++d == MM_HIGH_LEVEL) {
1473 struct tDisplayReply { uint8_t id; uint8_t mode; };
1474 tDisplayReply dr = { id : DRI_MMI_MODE_ACK, mode : MM_HIGH_LEVEL };
1475 dbgprotocol("Slot %d: ==> Display Reply (%d)\n", CamSlot()->SlotNumber(), SessionId());
1476 SendData(AOT_DISPLAY_REPLY, 2, (uint8_t *)&dr);
1477 }
1478 break;
1479 default: esyslog("ERROR: CAM %d: MMI: unsupported display control command %02X", CamSlot()->SlotNumber(), *d);
1480 }
1481 }
1482 }
1483 break;
1484 case AOT_LIST_LAST:
1485 case AOT_MENU_LAST: {
1486 dbgprotocol("Slot %d: <== Menu Last (%d)\n", CamSlot()->SlotNumber(), SessionId());
1487 delete menu;
1488 menu = new cCiMenu(this, Tag == AOT_MENU_LAST);
1489 int l = 0;
1490 const uint8_t *d = GetData(Data, l);
1491 if (l > 0) {
1492 // since the specification allows choiceNb to be undefined it is useless, so let's just skip it:
1493 d++;
1494 l--;
1495 if (l > 0) menu->titleText = GetText(l, &d);
1496 if (l > 0) menu->subTitleText = GetText(l, &d);
1497 if (l > 0) menu->bottomText = GetText(l, &d);
1498 int Action = CRA_NONE;
1499 int Select = -1;
1500 int Item = 0;
1501 while (l > 0) {
1502 char *s = GetText(l, &d);
1503 if (s) {
1504 if (!menu->AddEntry(s))
1505 free(s);
1506 else if (Action == CRA_NONE) {
1507 Action = CamResponses.GetMatch(CamSlot()->SlotNumber(), s);
1508 if (Action == CRA_SELECT)
1509 Select = Item;
1510 }
1511 }
1512 else
1513 break;
1514 Item++;
1515 }
1516 if (Action != CRA_NONE) {
1517 delete menu;
1518 menu = NULL;
1519 cCondWait::SleepMs(100);
1520 if (Action == CRA_DISCARD) {
1521 SendCloseMMI();
1522 dsyslog("CAM %d: DISCARD", CamSlot()->SlotNumber());
1523 }
1524 else if (Action == CRA_CONFIRM) {
1525 SendMenuAnswer(1);
1526 dsyslog("CAM %d: CONFIRM", CamSlot()->SlotNumber());
1527 }
1528 else if (Action == CRA_SELECT) {
1529 SendMenuAnswer(Select + 1);
1530 dsyslog("CAM %d: SELECT %d", CamSlot()->SlotNumber(), Select + 1);
1531 }
1532 }
1533 }
1534 }
1535 break;
1536 case AOT_ENQ: {
1537 dbgprotocol("Slot %d: <== Enq (%d)\n", CamSlot()->SlotNumber(), SessionId());
1538 delete enquiry;
1539 enquiry = new cCiEnquiry(this);
1540 int l = 0;
1541 const uint8_t *d = GetData(Data, l);
1542 if (l > 0) {
1543 uint8_t blind = *d++;
1544 //XXX GetByte()???
1545 l--;
1546 enquiry->blind = blind & EF_BLIND;
1547 enquiry->expectedLength = *d++;
1548 l--;
1549 // I really wonder why there is no text length field here...
1550 enquiry->text = CopyString(l, d);
1551 int Action = CamResponses.GetMatch(CamSlot()->SlotNumber(), enquiry->text);
1552 if (Action > CRA_NONE) {
1553 char s[enquiry->expectedLength * 2];
1554 snprintf(s, sizeof(s), "%d", Action);
1555 if (int(strlen(s)) == enquiry->expectedLength) {
1556 delete enquiry;
1557 enquiry = NULL;
1558 SendAnswer(s);
1559 dsyslog("CAM %d: PIN", CamSlot()->SlotNumber());
1560 }
1561 else
1562 esyslog("CAM %d: ERROR: unexpected PIN length %d, expected %d", CamSlot()->SlotNumber(), int(strlen(s)), enquiry->expectedLength);
1563 }
1564 }
1565 }
1566 break;
1567 case AOT_CLOSE_MMI: {
1568 int id = -1;
1569 int delay = -1;
1570 int l = 0;
1571 const uint8_t *d = GetData(Data, l);
1572 if (l > 0) {
1573 id = *d++;
1574 if (l > 1)
1575 delay = *d;
1576 }
1577 dbgprotocol("Slot %d: <== Close MMI (%d) id = %02X delay = %d\n", CamSlot()->SlotNumber(), SessionId(), id, delay);
1578 }
1579 break;
1580 default: esyslog("ERROR: CAM %d: MMI: unknown tag %06X", CamSlot()->SlotNumber(), Tag);
1581 }
1582 }
1583}
1584
1586{
1587 if (Clear)
1588 fetchedMenu = NULL;
1589 else if (menu) {
1590 fetchedMenu = menu;
1591 menu = NULL;
1592 }
1593 return fetchedMenu;
1594}
1595
1597{
1598 if (Clear)
1599 fetchedEnquiry = NULL;
1600 else if (enquiry) {
1602 enquiry = NULL;
1603 }
1604 return fetchedEnquiry;
1605}
1606
1607void cCiMMI::SendMenuAnswer(uint8_t Selection)
1608{
1609 dbgprotocol("Slot %d: ==> Menu Answ (%d)\n", CamSlot()->SlotNumber(), SessionId());
1610 SendData(AOT_MENU_ANSW, 1, &Selection);
1611}
1612
1613bool cCiMMI::SendAnswer(const char *Text)
1614{
1615 dbgprotocol("Slot %d: ==> Answ (%d)\n", CamSlot()->SlotNumber(), SessionId());
1616 struct tAnswer { uint8_t id; char text[256]; };//XXX
1617 tAnswer answer;
1618 answer.id = Text ? AI_ANSWER : AI_CANCEL;
1619 int len = 0;
1620 if (Text) {
1621 len = min(sizeof(answer.text), strlen(Text));
1622 memcpy(answer.text, Text, len);
1623 }
1624 SendData(AOT_ANSW, len + 1, (uint8_t *)&answer);
1625 return true;
1626}
1627
1629{
1630 dbgprotocol("Slot %d: ==> Close MMI (%d)\n", CamSlot()->SlotNumber(), SessionId());
1632 return true;
1633}
1634
1635// --- cCiMenu ---------------------------------------------------------------
1636
1638{
1639 mmi = MMI;
1640 mutex = NULL;
1643 numEntries = 0;
1644}
1645
1647{
1648 cMutexLock MutexLock(mutex);
1649 if (mmi)
1650 mmi->Menu(true);
1651 free(titleText);
1652 free(subTitleText);
1653 free(bottomText);
1654 for (int i = 0; i < numEntries; i++)
1655 free(entries[i]);
1656}
1657
1659{
1661 entries[numEntries++] = s;
1662 return true;
1663 }
1664 return false;
1665}
1666
1668{
1669 // If the mmi is gone, the menu shall be closed, which also qualifies as 'update'.
1670 return !mmi || mmi->HasUserIO();
1671}
1672
1673void cCiMenu::Select(int Index)
1674{
1675 cMutexLock MutexLock(mutex);
1676 if (mmi && -1 <= Index && Index < numEntries)
1677 mmi->SendMenuAnswer(Index + 1);
1678}
1679
1681{
1682 Select(-1);
1683}
1684
1686{
1687 cMutexLock MutexLock(mutex);
1688 if (mmi)
1689 mmi->SendCloseMMI();
1690}
1691
1692// --- cCiEnquiry ------------------------------------------------------------
1693
1695{
1696 mmi = MMI;
1697 mutex = NULL;
1698 text = NULL;
1699 blind = false;
1700 expectedLength = 0;
1701}
1702
1704{
1705 cMutexLock MutexLock(mutex);
1706 if (mmi)
1707 mmi->Enquiry(true);
1708 free(text);
1709}
1710
1711void cCiEnquiry::Reply(const char *s)
1712{
1713 cMutexLock MutexLock(mutex);
1714 if (mmi)
1715 mmi->SendAnswer(s);
1716}
1717
1719{
1720 Reply(NULL);
1721}
1722
1724{
1725 cMutexLock MutexLock(mutex);
1726 if (mmi)
1727 mmi->SendCloseMMI();
1728}
1729
1730// --- cCiResourceHandler ----------------------------------------------------
1731
1735
1739
1740// --- cCiDefaultResourceHandler ---------------------------------------------
1741
1743public:
1744 virtual const uint32_t *ResourceIds(void) const override;
1745 virtual cCiSession *GetNewCiSession(uint32_t ResourceId, uint16_t SessionId, cCiTransportConnection *Tc) override;
1746 };
1747
1749{
1750 static uint32_t Ids[] = {
1756 RI_MMI,
1757 0
1758 };
1759 return Ids;
1760}
1761
1763{
1764 switch (ResourceId) {
1765 case RI_RESOURCE_MANAGER: return new cCiResourceManager(SessionId, Tc); break;
1766 case RI_APPLICATION_INFORMATION: return new cCiApplicationInformation(SessionId, Tc); break;
1767 case RI_CONDITIONAL_ACCESS_SUPPORT: return new cCiConditionalAccessSupport(SessionId, Tc); break;
1768 case RI_HOST_CONTROL: return new cCiHostControl(SessionId, Tc); break;
1769 case RI_DATE_TIME: return new cCiDateTime(SessionId, Tc); break;
1770 case RI_MMI: return new cCiMMI(SessionId, Tc); break;
1771 default: return NULL;
1772 }
1773}
1774
1775// --- cCiResourceHandlers ---------------------------------------------------
1776
1778
1783
1785{
1786 if (ResourceHandler) {
1787 Add(ResourceHandler);
1788 if (const uint32_t *r = ResourceHandler->ResourceIds()) {
1789 while (*r) {
1790 resourceIds.Append(htonl(*r));
1791 r++;
1792 }
1793 }
1794 }
1795}
1796
1797cCiSession *cCiResourceHandlers::GetNewCiSession(uint32_t ResourceId, uint16_t SessionId, cCiTransportConnection *Tc)
1798{
1799 for (cCiResourceHandler *r = Last(); r; r = Prev(r)) {
1800 if (cCiSession *CiSession = r->GetNewCiSession(ResourceId, SessionId, Tc))
1801 return CiSession;
1802 }
1803 return NULL;
1804}
1805
1806// --- cCiTransportConnection (cont'd) ---------------------------------------
1807
1808#define TC_POLL_TIMEOUT 300 // ms WORKAROUND: TC_POLL_TIMEOUT < 300ms doesn't work with DragonCAM
1809#define TC_ALIVE_TIMEOUT 2000 // ms after which a transport connection is assumed dead
1810
1812{
1813 dbgprotocol("Slot %d: creating connection %d/%d\n", CamSlot->SlotNumber(), CamSlot->SlotIndex(), Tcid);
1814 camSlot = CamSlot;
1815 tcid = Tcid;
1816 state = stIDLE;
1819 hasUserIO = false;
1821 for (int i = 0; i <= MAX_SESSIONS_PER_TC; i++) // sessions[0] is not used, but initialized anyway
1822 sessions[i] = NULL;
1823 tsPostProcessor = NULL;
1824}
1825
1827{
1828 for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++)
1829 delete sessions[i];
1830}
1831
1833{
1834 tsPostProcessor = CiSession;
1835}
1836
1838{
1839 cMutexLock MutexLock(&mutex);
1840 if (tsPostProcessor)
1841 return tsPostProcessor->TsPostProcess(TsPacket);
1842 return false;
1843}
1844
1850
1856
1857void cCiTransportConnection::SendTPDU(uint8_t Tag, int Length, const uint8_t *Data)
1858{
1859 cTPDU TPDU(camSlot->SlotIndex(), tcid, Tag, Length, Data);
1860 camSlot->Write(&TPDU);
1862}
1863
1864void cCiTransportConnection::SendData(int Length, const uint8_t *Data)
1865{
1866 // if Length ever exceeds MAX_TPDU_DATA this needs to be handled differently
1867 if (state == stACTIVE && Length > 0)
1868 SendTPDU(T_DATA_LAST, Length, Data);
1869}
1870
1871void cCiTransportConnection::SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId, int Status)
1872{
1873 uint8_t buffer[16];
1874 uint8_t *p = buffer;
1875 *p++ = Tag;
1876 *p++ = 0x00; // will contain length
1877 if (Status >= 0)
1878 *p++ = Status;
1879 if (ResourceId) {
1880 put_unaligned(htonl(ResourceId), (uint32_t *)p);
1881 p += 4;
1882 }
1883 put_unaligned(htons(SessionId), (uint16_t *)p);
1884 p += 2;
1885 buffer[1] = p - buffer - 2; // length
1886 SendData(p - buffer, buffer);
1887}
1888
1890{
1891 bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer;
1893 if (DumpPolls)
1894 dbgprotocol("Slot %d: ==> Poll\n", camSlot->SlotNumber());
1896 DumpTPDUDataTransfer = OldDumpTPDUDataTransfer;
1897}
1898
1899uint32_t cCiTransportConnection::ResourceIdToInt(const uint8_t *Data)
1900{
1901 return (ntohl(get_unaligned((uint32_t *)Data)));
1902}
1903
1905{
1906 return (SessionId <= MAX_SESSIONS_PER_TC) ? sessions[SessionId] : NULL;
1907}
1908
1910{
1911 cCiSession *CiSession = NULL;
1912 for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
1913 if (cCiSession *s = sessions[i]) {
1914 if (s->ResourceId() == ResourceId)
1915 return s; // prefer exact match
1916 if ((s->ResourceId() & RESOURCE_CLASS_MASK) == (ResourceId & RESOURCE_CLASS_MASK))
1917 CiSession = s;
1918 }
1919 }
1920 return CiSession;
1921}
1922
1923void cCiTransportConnection::OpenSession(int Length, const uint8_t *Data)
1924{
1925 if (Length == 6 && *(Data + 1) == 0x04) {
1926 uint32_t ResourceId = ResourceIdToInt(Data + 2);
1927 dbgprotocol("Slot %d: open session %08X\n", camSlot->SlotNumber(), ResourceId);
1928 if (!GetSessionByResourceId(ResourceId)) {
1929 for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
1930 if (!sessions[i]) {
1931 sessions[i] = CiResourceHandlers.GetNewCiSession(ResourceId, i, this);
1932 if (sessions[i])
1933 SendTag(ST_OPEN_SESSION_RESPONSE, sessions[i]->SessionId(), sessions[i]->ResourceId(), SS_OK);
1934 else
1935 esyslog("ERROR: CAM %d: unknown resource identifier: %08X (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
1936 return;
1937 }
1938 }
1939 esyslog("ERROR: CAM %d: no free session slot for resource identifier %08X (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
1940 }
1941 else
1942 esyslog("ERROR: CAM %d: session for resource identifier %08X already exists (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid);
1943 }
1944}
1945
1947{
1948 dbgprotocol("Slot %d: close session %d\n", camSlot->SlotNumber(), SessionId);
1949 cCiSession *Session = GetSessionBySessionId(SessionId);
1950 if (Session && sessions[SessionId] == Session) {
1951 delete Session;
1952 sessions[SessionId] = NULL;
1953 SendTag(ST_CLOSE_SESSION_RESPONSE, SessionId, 0, SS_OK);
1954 }
1955 else {
1956 esyslog("ERROR: CAM %d: unknown session id: %d (%d/%d)", camSlot->SlotNumber(), SessionId, camSlot->SlotIndex(), tcid);
1958 }
1959}
1960
1962{
1963 int Length;
1964 const uint8_t *Data = TPDU->Data(Length);
1965 if (Data && Length > 1) {
1966 switch (*Data) {
1967 case ST_SESSION_NUMBER: if (Length > 4) {
1968 uint16_t SessionId = ntohs(get_unaligned((uint16_t *)&Data[2]));
1969 cCiSession *Session = GetSessionBySessionId(SessionId);
1970 if (Session)
1971 Session->Process(Length - 4, Data + 4);
1972 else
1973 esyslog("ERROR: CAM %d: unknown session id: %d (%d/%d)", camSlot->SlotNumber(), SessionId, camSlot->SlotIndex(), tcid);
1974 }
1975 break;
1976 case ST_OPEN_SESSION_REQUEST: OpenSession(Length, Data);
1977 break;
1978 case ST_CLOSE_SESSION_REQUEST: if (Length == 4)
1979 CloseSession(ntohs(get_unaligned((uint16_t *)&Data[2])));
1980 break;
1981 case ST_CREATE_SESSION_RESPONSE: // not implemented
1982 case ST_CLOSE_SESSION_RESPONSE: // not implemented
1983 default: esyslog("ERROR: CAM %d: unknown session tag: %02X (%d/%d)", camSlot->SlotNumber(), *Data, camSlot->SlotIndex(), tcid);
1984 }
1985 }
1986}
1987
1989{
1990 if (TPDU)
1992 else if (alive.TimedOut())
1993 return false;
1994 switch (state) {
1995 case stIDLE:
1997 dbgprotocol("Slot %d: create connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
2000 state = stCREATION;
2001 }
2002 return true;
2003 case stCREATION:
2004 if (TPDU && TPDU->Tag() == T_CTC_REPLY) {
2005 dbgprotocol("Slot %d: connection created %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
2006 Poll();
2007 state = stACTIVE;
2008 }
2009 else if (timer.TimedOut()) {
2010 dbgprotocol("Slot %d: timeout while creating connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
2011 state = stIDLE;
2012 }
2013 return true;
2014 case stACTIVE:
2016 dbgprotocol("Slot %d: delete connection requested %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
2019 state = stDELETION;
2020 return true;
2021 }
2022 if (TPDU) {
2023 switch (TPDU->Tag()) {
2024 case T_REQUEST_TC:
2025 esyslog("ERROR: CAM %d: T_REQUEST_TC not implemented (%d/%d)", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
2026 break;
2027 case T_DATA_MORE:
2028 case T_DATA_LAST:
2029 HandleSessions(TPDU);
2030 // continue with T_SB
2031 case T_SB:
2032 if ((TPDU->Status() & DATA_INDICATOR) != 0) {
2033 dbgprotocol("Slot %d: receive data %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
2034 SendTPDU(T_RCV);
2035 }
2036 break;
2037 case T_DELETE_TC:
2038 dbgprotocol("Slot %d: delete connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
2040 state = stIDLE;
2041 return true;
2042 case T_RCV:
2043 case T_CREATE_TC:
2044 case T_CTC_REPLY:
2045 case T_DTC_REPLY:
2046 case T_NEW_TC:
2047 case T_TC_ERROR:
2048 break;
2049 default:
2050 esyslog("ERROR: unknown TPDU tag: 0x%02X (%s)", TPDU->Tag(), __FUNCTION__);
2051 }
2052 }
2053 else if (timer.TimedOut())
2054 Poll();
2055 hasUserIO = false;
2056 for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) {
2057 if (sessions[i]) {
2058 sessions[i]->Process();
2059 if (sessions[i]->HasUserIO())
2060 hasUserIO = true;
2061 }
2062 }
2063 break;
2064 case stDELETION:
2065 if (TPDU && TPDU->Tag() == T_DTC_REPLY || timer.TimedOut()) {
2066 dbgprotocol("Slot %d: connection deleted %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
2067 state = stIDLE;
2068 }
2069 return true;
2070 default:
2071 esyslog("ERROR: unknown state: %d (%s)", state, __FUNCTION__);
2072 }
2073 return true;
2074}
2075
2076// --- cCiCaPidData ----------------------------------------------------------
2077
2079public:
2081 int pid;
2083 cCiCaPidData(int Pid, int StreamType)
2084 {
2085 active = false;
2086 pid = Pid;
2087 streamType = StreamType;
2088 }
2089 };
2090
2091// --- cCiCaProgramData ------------------------------------------------------
2092
2094public:
2098 cCiCaProgramData(int ProgramNumber)
2099 {
2100 programNumber = ProgramNumber;
2101 modified = false;
2102 }
2103 bool Active(void)
2104 {
2105 for (cCiCaPidData *p = pidList.First(); p; p = pidList.Next(p)) {
2106 if (p->active)
2107 return true;
2108 }
2109 return false;
2110 }
2111 };
2112
2113// --- cCiAdapter ------------------------------------------------------------
2114
2116:cThread("CI adapter")
2117{
2118 for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++)
2119 camSlots[i] = NULL;
2120}
2121
2123{
2124 Cancel(3);
2125 for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++)
2126 delete camSlots[i];
2127}
2128
2130{
2131 if (CamSlot) {
2132 for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) {
2133 if (!camSlots[i]) {
2134 CamSlot->slotIndex = i;
2135 camSlots[i] = CamSlot;
2136 return;
2137 }
2138 }
2139 esyslog("ERROR: no free CAM slot in CI adapter");
2140 }
2141}
2142
2144{
2145 if (Iter >= 0) {
2146 for (; Iter < MAX_CAM_SLOTS_PER_ADAPTER; ) {
2147 if (cCamSlot *Found = camSlots[Iter++])
2148 return Found;
2149 }
2150 }
2151 return NULL;
2152}
2153
2155{
2156 cTPDU TPDU;
2157 while (Running()) {
2158 int n = Read(TPDU.Buffer(), TPDU.MaxSize());
2159 if (n > 0 && TPDU.Slot() < MAX_CAM_SLOTS_PER_ADAPTER) {
2160 TPDU.SetSize(n);
2161 cCamSlot *cs = camSlots[TPDU.Slot()];
2162 TPDU.Dump(cs ? cs->SlotNumber() : 0, false);
2163 if (cs)
2164 cs->Process(&TPDU);
2165 }
2166 for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) {
2167 if (camSlots[i])
2168 camSlots[i]->Process();
2169 }
2170 }
2171}
2172
2173// --- cCamSlot --------------------------------------------------------------
2174
2175#define MODULE_CHECK_INTERVAL 500 // ms
2176#define MODULE_RESET_TIMEOUT 2 // s
2177
2179{
2180 ciAdapter = CiAdapter;
2182 assignedDevice = NULL;
2184 caActivationReceiver = NULL;
2185 slotIndex = -1;
2186 mtdAvailable = false;
2187 mtdHandler = NULL;
2188 lastModuleStatus = msReset; // avoids initial reset log message
2189 resetTime = 0;
2190 resendPmt = false;
2191 for (int i = 0; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) // tc[0] is not used, but initialized anyway
2192 tc[i] = NULL;
2193 if (MasterSlot)
2195 if (ciAdapter) {
2196 CamSlots.Add(this);
2197 slotNumber = Index() + 1;
2198 ciAdapter->AddCamSlot(this);
2199 Reset();
2200 }
2201}
2202
2204{
2205 Assign(NULL);
2206 delete caPidReceiver;
2207 delete caActivationReceiver;
2208 CamSlots.Del(this, false);
2210 delete mtdHandler;
2211}
2212
2214{
2215 cMutexLock MutexLock(&mutex);
2216 if (mtdHandler)
2217 return mtdHandler->GetMtdCamSlot(this);
2218 return this;
2219}
2220
2222{
2223 // 'mutex' isn't held across the cDevice calls below: they take the device's
2224 // receiver lock, under which AttachReceiver() -> AddPid() -> SetPid() takes
2225 // 'mutex'. Readers may briefly see no assigned device, which is harmless:
2226 cMutexLock AssignMutexLock(&assignMutex);
2227 cDevice *OldDevice = NULL;
2228 {
2229 cMutexLock MutexLock(&mutex);
2230 if (Device == assignedDevice)
2231 return true;
2232 if (!ciAdapter)
2233 return false;
2234 if (assignedDevice && !Query) {
2235 OldDevice = assignedDevice;
2236 assignedDevice = NULL;
2237 }
2238 }
2239 int OldDeviceNumber = 0;
2240 if (OldDevice) {
2241 OldDeviceNumber = OldDevice->DeviceNumber() + 1;
2242 if (caPidReceiver)
2243 OldDevice->Detach(caPidReceiver);
2244 OldDevice->SetCamSlot(NULL);
2245 }
2246 if (ciAdapter->Assign(Device, true)) {
2247 if (!Query) {
2249 if (ciAdapter->Assign(Device)) {
2250 if (Device) {
2251 Device->SetCamSlot(this);
2252 {
2253 cMutexLock MutexLock(&mutex);
2255 }
2256 if (caPidReceiver) {
2259 }
2260 dsyslog("CAM %d: assigned to device %d", MasterSlotNumber(), Device->DeviceNumber() + 1);
2261 }
2262 else {
2264 dsyslog("CAM %d: unassigned from device %d", MasterSlotNumber(), OldDeviceNumber);
2265 }
2266 }
2267 else
2268 return false;
2269 }
2270 return true;
2271 }
2272 return false;
2273}
2274
2276{
2277 cMutexLock MutexLock(&mutex);
2278 if (mtdHandler)
2279 return mtdHandler->Devices(DeviceNumbers);
2280 if (cDevice *d = assignedDevice)
2281 DeviceNumbers.Append(d->DeviceNumber());
2282 return DeviceNumbers.Size() > 0;
2283}
2284
2286{
2287 cMutexLock MutexLock(&mutex);
2288 for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) {
2289 if (!tc[i]) {
2290 tc[i] = new cCiTransportConnection(this, i);
2291 tc[i]->CreateConnection();
2292 return;
2293 }
2294 }
2295 esyslog("ERROR: CAM %d: can't create new transport connection!", slotNumber);
2296}
2297
2299{
2300 cMutexLock MutexLock(&mutex);
2301 for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) {
2302 delete tc[i];
2303 tc[i] = NULL;
2304 }
2305}
2306
2308{
2309 cMutexLock MutexLock(&mutex);
2310 if (TPDU) {
2311 int n = TPDU->Tcid();
2312 if (1 <= n && n <= MAX_CONNECTIONS_PER_CAM_SLOT) {
2313 if (tc[n])
2314 tc[n]->Process(TPDU);
2315 }
2316 }
2317 for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) {
2318 if (tc[i]) {
2319 if (!tc[i]->Process()) {
2320 Reset();
2321 return;
2322 }
2323 }
2324 }
2325 if (moduleCheckTimer.TimedOut()) {
2327 if (ms != lastModuleStatus) {
2328 switch (ms) {
2329 case msNone:
2330 dbgprotocol("Slot %d: no module present\n", slotNumber);
2331 isyslog("CAM %d: no module present", slotNumber);
2335 if (mtdHandler)
2337 else
2338 Assign(NULL);
2339 break;
2340 case msReset:
2341 dbgprotocol("Slot %d: module reset\n", slotNumber);
2342 isyslog("CAM %d: module reset", slotNumber);
2344 break;
2345 case msPresent:
2346 dbgprotocol("Slot %d: module present\n", slotNumber);
2347 isyslog("CAM %d: module present", slotNumber);
2348 break;
2349 case msReady:
2350 dbgprotocol("Slot %d: module ready\n", slotNumber);
2351 isyslog("CAM %d: module ready", slotNumber);
2352 NewConnection();
2353 resendPmt = true;
2354 break;
2355 default:
2356 esyslog("ERROR: unknown module status %d (%s)", ms, __FUNCTION__);
2357 }
2358 lastModuleStatus = ms;
2359 }
2361 }
2362 if (resendPmt && Ready()) {
2363 if (mtdHandler) {
2365 resendPmt = false;
2366 }
2367 else if (caProgramList.Count())
2369 }
2371}
2372
2374{
2375 cMutexLock MutexLock(&mutex);
2376 return tc[1] ? tc[1]->GetSessionByResourceId(ResourceId) : NULL;
2377}
2378
2380{
2381 cMutexLock MutexLock(&mutex);
2382 if (ciAdapter && TPDU->Size()) {
2383 TPDU->Dump(SlotNumber(), true);
2384 ciAdapter->Write(TPDU->Buffer(), TPDU->Size());
2385 }
2386}
2387
2389{
2390 cMutexLock MutexLock(&mutex);
2393 if (ciAdapter) {
2394 dbgprotocol("Slot %d: reset...", slotNumber);
2395 if (ciAdapter->Reset(slotIndex)) {
2396 resetTime = time(NULL);
2397 dbgprotocol("ok.\n");
2399 return true;
2400 }
2401 dbgprotocol("failed!\n");
2402 }
2403 return false;
2404}
2405
2407{
2408 return ModuleStatus() == msReady;
2409}
2410
2412{
2413 cMutexLock MutexLock(&mutex);
2414 if (!caActivationReceiver) {
2415 if (cDevice *d = Device()) {
2417 if (const cChannel *Channel = Channels->GetByNumber(cDevice::CurrentChannel())) {
2418 caActivationReceiver = new cCaActivationReceiver(Channel, this);
2419 d->AttachReceiver(caActivationReceiver);
2420 dsyslog("CAM %d: activating on device %d with channel %d (%s)", SlotNumber(), d->DeviceNumber() + 1, Channel->Number(), Channel->Name());
2421 }
2422 }
2423 }
2424}
2425
2427{
2428 cMutexLock MutexLock(&mutex);
2429 if (mtdHandler)
2431 else {
2432 delete caActivationReceiver;
2433 caActivationReceiver = NULL;
2434 }
2435}
2436
2438{
2439 if (mtdHandler)
2440 return mtdHandler->IsActivating();
2441 return caActivationReceiver;
2442}
2443
2445{
2446 cMutexLock MutexLock(&mutex);
2448 if (resetTime) {
2449 if (ms <= msReset) {
2450 if (time(NULL) - resetTime < MODULE_RESET_TIMEOUT)
2451 return msReset;
2452 }
2453 resetTime = 0;
2454 }
2455 return ms;
2456}
2457
2458const char *cCamSlot::GetCamName(void)
2459{
2460 cMutexLock MutexLock(&mutex);
2461 return tc[1] ? tc[1]->GetCamName() : NULL;
2462}
2463
2465{
2466 cMutexLock MutexLock(&mutex);
2467 return ModuleStatus() == msNone || tc[1] && tc[1]->Ready();
2468}
2469
2471{
2473}
2474
2476{
2477 cMutexLock MutexLock(&mutex);
2478 return tc[1] && tc[1]->HasUserIO();
2479}
2480
2482{
2483 cMutexLock MutexLock(&mutex);
2485 return api ? api->EnterMenu() : false;
2486}
2487
2489{
2490 cMutexLock MutexLock(&mutex);
2492 if (mmi) {
2493 cCiMenu *Menu = mmi->Menu();
2494 if (Menu)
2495 Menu->mutex = &mutex;
2496 return Menu;
2497 }
2498 return NULL;
2499}
2500
2502{
2503 cMutexLock MutexLock(&mutex);
2505 if (mmi) {
2506 cCiEnquiry *Enquiry = mmi->Enquiry();
2507 if (Enquiry)
2508 Enquiry->mutex = &mutex;
2509 return Enquiry;
2510 }
2511 return NULL;
2512}
2513
2515{
2516 for (int i = 0; i < caPmts.Size(); i++)
2517 delete caPmts[i];
2518}
2519
2520cCiCaPmt *cCiCaPmtList::Add(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds)
2521{
2522 cCiCaPmt *p = new cCiCaPmt(CmdId, Source, Transponder, ProgramNumber, CaSystemIds);
2523 caPmts.Append(p);
2524 return p;
2525}
2526
2528{
2529 if (caPmts.RemoveElement(CaPmt))
2530 delete CaPmt;
2531}
2532
2539
2540void cCamSlot::BuildCaPmts(uint8_t CmdId, cCiCaPmtList &CaPmtList, cMtdMapper *MtdMapper)
2541{
2542 cMutexLock MutexLock(&mutex);
2543 CaPmtList.caPmts.Clear();
2544 const int *CaSystemIds = GetCaSystemIds();
2545 if (CaSystemIds && *CaSystemIds) {
2546 if (caProgramList.Count()) {
2547 for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
2548 if (p->modified || resendPmt) {
2549 bool Active = p->Active();
2550 cCiCaPmt *CaPmt = CaPmtList.Add(Active ? CmdId : CPCI_NOT_SELECTED, source, transponder, p->programNumber, CaSystemIds);
2551 for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
2552 if (q->active)
2553 CaPmt->AddPid(q->pid, q->streamType);
2554 }
2555 if (caPidReceiver) {
2556 int CaPids[MAXRECEIVEPIDS + 1];
2557 if (GetCaPids(source, transponder, p->programNumber, CaSystemIds, MAXRECEIVEPIDS + 1, CaPids) > 0) {
2558 if (Active)
2559 caPidReceiver->AddPids(CaPids);
2560 else {
2561 KeepSharedCaPids(p->programNumber, CaSystemIds, CaPids);
2562 caPidReceiver->DelPids(CaPids);
2563 }
2564 }
2565 }
2566 if (RepliesToQuery())
2567 CaPmt->SetListManagement(Active ? CPLM_ADD : CPLM_UPDATE);
2568 if (MtdMapper)
2569 CaPmt->MtdMapPids(MtdMapper);
2570 p->modified = false;
2571 }
2572 }
2573 }
2574 else if (CmdId == CPCI_NOT_SELECTED)
2575 CaPmtList.Add(CmdId, 0, 0, 0, NULL);
2576 }
2577}
2578
2579void cCamSlot::KeepSharedCaPids(int ProgramNumber, const int *CaSystemIds, int *CaPids)
2580{
2581 int numPids = 0;
2582 int *pCaPids = CaPids;
2583 while (*pCaPids) {
2584 numPids++;
2585 pCaPids++;
2586 }
2587 if (numPids <= 0)
2588 return;
2589 int CaPids2[MAXRECEIVEPIDS + 1];
2590 for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
2591 if (p->Active()) {
2592 if (GetCaPids(source, transponder, p->programNumber, CaSystemIds, MAXRECEIVEPIDS + 1, CaPids2) > 0) {
2593 int *pCaPids2 = CaPids2;
2594 while (*pCaPids2) {
2595 pCaPids = CaPids;
2596 while (*pCaPids) {
2597 if (*pCaPids == *pCaPids2) {
2598 dsyslog("CAM %d: keeping shared CA pid %d", SlotNumber(), *pCaPids);
2599 // To remove *pCaPids from CaPids we overwrite it with the last valie in the list, and then strip the last value:
2600 *pCaPids = CaPids[numPids - 1];
2601 numPids--;
2602 CaPids[numPids] = 0;
2603 if (numPids <= 0)
2604 return;
2605 }
2606 else
2607 pCaPids++;
2608 }
2609 pCaPids2++;
2610 }
2611 }
2612 }
2613 }
2614}
2615
2617{
2618 cMutexLock MutexLock(&mutex);
2620 if (cas) {
2621 for (int i = 0; i < CaPmtList.caPmts.Size(); i++)
2622 cas->SendPMT(CaPmtList.caPmts[i]);
2623 }
2624 resendPmt = false;
2625}
2626
2627void cCamSlot::SendCaPmt(uint8_t CmdId)
2628{
2629 cMutexLock MutexLock(&mutex);
2630 cCiCaPmtList CaPmtList;
2631 BuildCaPmts(CmdId, CaPmtList);
2632 SendCaPmts(CaPmtList);
2633}
2634
2636{
2637 mtdAvailable = true;
2638}
2639
2641{
2642 if (McdAvailable() && MtdAvailable()) {
2643 if (On) {
2644 if (!mtdHandler) {
2645 dsyslog("CAM %d: activating MTD support", SlotNumber());
2646 mtdHandler = new cMtdHandler;
2647 }
2648 }
2649 else if (mtdHandler) {
2650 dsyslog("CAM %d: deactivating MTD support", SlotNumber());
2651 delete mtdHandler;
2652 mtdHandler = NULL;
2653 }
2654 }
2655}
2656
2657int cCamSlot::MtdPutData(uchar *Data, int Count)
2658{
2659 return mtdHandler->Put(Data, Count);
2660}
2661
2668
2670{
2671 if (mtdHandler)
2672 return mtdHandler->Priority();
2673 cDevice *d = Device();
2674 return d ? d->Priority() : IDLEPRIORITY;
2675}
2676
2677bool cCamSlot::ProvidesCa(const int *CaSystemIds)
2678{
2679 cMutexLock MutexLock(&mutex);
2681 if (cas) {
2682 for (const int *ids = cas->GetCaSystemIds(); ids && *ids; ids++) {
2683 for (const int *id = CaSystemIds; *id; id++) {
2684 if (*id == *ids)
2685 return true;
2686 }
2687 }
2688 }
2689 return false;
2690}
2691
2692void cCamSlot::AddPid(int ProgramNumber, int Pid, int StreamType)
2693{
2694 cMutexLock MutexLock(&mutex);
2695 cCiCaProgramData *ProgramData = NULL;
2696 for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
2697 if (p->programNumber == ProgramNumber) {
2698 ProgramData = p;
2699 for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
2700 if (q->pid == Pid)
2701 return;
2702 }
2703 }
2704 }
2705 if (!ProgramData)
2706 caProgramList.Add(ProgramData = new cCiCaProgramData(ProgramNumber));
2707 ProgramData->pidList.Add(new cCiCaPidData(Pid, StreamType));
2708}
2709
2710void cCamSlot::SetPid(int Pid, bool Active)
2711{
2713 return;
2714 cMutexLock MutexLock(&mutex);
2715 for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
2716 for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
2717 if (q->pid == Pid) {
2718 if (q->active != Active) {
2719 q->active = Active;
2720 p->modified = true;
2721 }
2722 return;
2723 }
2724 }
2725 }
2726}
2727
2728// see ISO/IEC 13818-1
2729#define STREAM_TYPE_VIDEO 0x02
2730#define STREAM_TYPE_AUDIO 0x04
2731#define STREAM_TYPE_PRIVATE 0x06
2732
2733void cCamSlot::AddChannel(const cChannel *Channel)
2734{
2735 cMutexLock MutexLock(&mutex);
2736 if (source != Channel->Source() || transponder != Channel->Transponder())
2738 source = Channel->Source();
2739 transponder = Channel->Transponder();
2740 if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
2741 AddPid(Channel->Sid(), Channel->Vpid(), STREAM_TYPE_VIDEO);
2742 for (const int *Apid = Channel->Apids(); *Apid; Apid++)
2743 AddPid(Channel->Sid(), *Apid, STREAM_TYPE_AUDIO);
2744 for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
2745 AddPid(Channel->Sid(), *Dpid, STREAM_TYPE_PRIVATE);
2746 for (const int *Spid = Channel->Spids(); *Spid; Spid++)
2747 AddPid(Channel->Sid(), *Spid, STREAM_TYPE_PRIVATE);
2748 }
2749}
2750
2751#define QUERY_REPLY_WAIT 100 // ms to wait between checks for a reply
2752
2753bool cCamSlot::CanDecrypt(const cChannel *Channel, cMtdMapper *MtdMapper)
2754{
2755 if (Channel->Ca() < CA_ENCRYPTED_MIN)
2756 return true; // channel not encrypted
2757 if (!IsDecrypting())
2758 return true; // any CAM can decrypt at least one channel
2759 cMutexLock MutexLock(&mutex);
2761 if (cas && cas->RepliesToQuery()) {
2762 cCiCaPmt CaPmt(CPCI_QUERY, Channel->Source(), Channel->Transponder(), Channel->Sid(), GetCaSystemIds());
2763 CaPmt.SetListManagement(CPLM_ADD); // WORKAROUND: CPLM_ONLY doesn't work with Alphacrypt 3.09 (deletes existing CA_PMTs)
2764 CaPmt.AddPid(Channel->Vpid(), STREAM_TYPE_VIDEO);
2765 for (const int *Apid = Channel->Apids(); *Apid; Apid++)
2766 CaPmt.AddPid(*Apid, STREAM_TYPE_AUDIO);
2767 for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
2768 CaPmt.AddPid(*Dpid, STREAM_TYPE_PRIVATE);
2769 for (const int *Spid = Channel->Spids(); *Spid; Spid++)
2770 CaPmt.AddPid(*Spid, STREAM_TYPE_PRIVATE);
2771 if (MtdMapper)
2772 CaPmt.MtdMapPids(MtdMapper);
2773 cas->SendPMT(&CaPmt);
2775 do {
2777 if ((cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT)) != NULL) { // must re-fetch it, there might have been a reset
2778 if (cas->ReceivedReply())
2779 return cas->CanDecrypt();
2780 }
2781 else
2782 return false;
2783 } while (!Timeout.TimedOut());
2784 dsyslog("CAM %d: didn't reply to QUERY", SlotNumber());
2785 }
2786 return false;
2787}
2788
2793
2795{
2796 cMutexLock MutexLock(&mutex);
2797 if (mtdHandler) {
2799 return;
2800 }
2801 if (caProgramList.Count()) {
2803 if (!dynamic_cast<cMtdCamSlot *>(this) || !MasterSlot()->IsDecrypting())
2805 }
2806}
2807
2809{
2810 cMutexLock MutexLock(&mutex);
2811 if (mtdHandler)
2812 return mtdHandler->IsDecrypting();
2813 if (caProgramList.Count()) {
2814 for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
2815 if (p->modified)
2816 return true; // any modifications need to be processed before we can assume it's no longer decrypting
2817 for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
2818 if (q->active)
2819 return true;
2820 }
2821 }
2822 }
2823 return false;
2824}
2825
2826uchar *cCamSlot::Decrypt(uchar *Data, int &Count)
2827{
2828 if (Data)
2829 Count = TS_SIZE;
2830 return Data;
2831}
2832
2834{
2835 return tc[1] ? tc[1]->TsPostProcess(Data) : false;
2836}
2837
2838bool cCamSlot::Inject(uchar *Data, int Count)
2839{
2840 return true;
2841}
2842
2844{
2845 cEitGenerator Eit(Sid);
2846 Inject(Eit.Data(), Eit.Length());
2847}
2848
2849// --- cCamSlots -------------------------------------------------------------
2850
2852
2854{
2855 int n = 0;
2856 for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
2857 if (CamSlot->IsMasterSlot() && CamSlot->ModuleStatus() == msReady)
2858 n++;
2859 }
2860 return n;
2861}
2862
2864{
2865 bool ready = true;
2866 for (time_t t0 = time(NULL); time(NULL) - t0 < Timeout; ) {
2867 ready = true;
2868 for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
2869 if (!CamSlot->Ready()) {
2870 ready = false;
2871 cCondWait::SleepMs(100);
2872 }
2873 }
2874 if (ready)
2875 break;
2876 }
2877 for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot))
2878 dsyslog("CAM %d: %sready, %s", CamSlot->SlotNumber(), CamSlot->Ready() ? "" : "not ", CamSlot->IsMasterSlot() ? *cString::sprintf("master (%s)", CamSlot->GetCamName() ? CamSlot->GetCamName() : "empty") : *cString::sprintf("slave of CAM %d", CamSlot->MasterSlotNumber()));
2879 return ready;
2880}
2881
2882// --- cChannelCamRelation ---------------------------------------------------
2883
2884#define CAM_CHECKED_TIMEOUT 15 // seconds before a CAM that has been checked for a particular channel will be checked again
2885
2887private:
2892public:
2894 bool TimedOut(void);
2896 bool CamChecked(int CamSlotNumber);
2897 bool CamDecrypt(int CamSlotNumber);
2898 void SetChecked(int CamSlotNumber);
2899 void SetDecrypt(int CamSlotNumber);
2900 void ClrChecked(int CamSlotNumber);
2901 void ClrDecrypt(int CamSlotNumber);
2902 };
2903
2911
2913{
2914 return !camSlotsDecrypt && time(NULL) - lastChecked > CAM_CHECKED_TIMEOUT;
2915}
2916
2917bool cChannelCamRelation::CamChecked(int CamSlotNumber)
2918{
2919 if (lastChecked && time(NULL) - lastChecked > CAM_CHECKED_TIMEOUT) {
2920 lastChecked = 0;
2921 camSlotsChecked = 0;
2922 }
2923 return camSlotsChecked & (1 << (CamSlotNumber - 1));
2924}
2925
2926bool cChannelCamRelation::CamDecrypt(int CamSlotNumber)
2927{
2928 return camSlotsDecrypt & (1 << (CamSlotNumber - 1));
2929}
2930
2931void cChannelCamRelation::SetChecked(int CamSlotNumber)
2932{
2933 camSlotsChecked |= (1 << (CamSlotNumber - 1));
2934 lastChecked = time(NULL);
2935 ClrDecrypt(CamSlotNumber);
2936}
2937
2938void cChannelCamRelation::SetDecrypt(int CamSlotNumber)
2939{
2940 camSlotsDecrypt |= (1 << (CamSlotNumber - 1));
2941 ClrChecked(CamSlotNumber);
2942}
2943
2944void cChannelCamRelation::ClrChecked(int CamSlotNumber)
2945{
2946 camSlotsChecked &= ~(1 << (CamSlotNumber - 1));
2947 lastChecked = 0;
2948}
2949
2950void cChannelCamRelation::ClrDecrypt(int CamSlotNumber)
2951{
2952 camSlotsDecrypt &= ~(1 << (CamSlotNumber - 1));
2953}
2954
2955// --- cChannelCamRelations --------------------------------------------------
2956
2957#define MAX_CAM_NUMBER 32
2958#define CHANNEL_CAM_RELATIONS_CLEANUP_INTERVAL 3600 // seconds between cleanups
2959
2961
2963{
2964 lastCleanup = time(NULL);
2965}
2966
2968{
2969 cMutexLock MutexLock(&mutex);
2971 for (cChannelCamRelation *ccr = First(); ccr; ) {
2972 cChannelCamRelation *c = ccr;
2973 ccr = Next(ccr);
2974 if (c->TimedOut())
2975 Del(c);
2976 }
2977 lastCleanup = time(NULL);
2978 }
2979}
2980
2982{
2983 cMutexLock MutexLock(&mutex);
2984 Cleanup();
2985 for (cChannelCamRelation *ccr = First(); ccr; ccr = Next(ccr)) {
2986 if (ccr->ChannelID() == ChannelID)
2987 return ccr;
2988 }
2989 return NULL;
2990}
2991
2993{
2994 cMutexLock MutexLock(&mutex);
2995 cChannelCamRelation *ccr = GetEntry(ChannelID);
2996 if (!ccr)
2997 Add(ccr = new cChannelCamRelation(ChannelID));
2998 return ccr;
2999}
3000
3001void cChannelCamRelations::Reset(int CamSlotNumber)
3002{
3003 cMutexLock MutexLock(&mutex);
3004 for (cChannelCamRelation *ccr = First(); ccr; ccr = Next(ccr)) {
3005 ccr->ClrChecked(CamSlotNumber);
3006 ccr->ClrDecrypt(CamSlotNumber);
3007 }
3008}
3009
3010bool cChannelCamRelations::CamChecked(tChannelID ChannelID, int CamSlotNumber)
3011{
3012 cMutexLock MutexLock(&mutex);
3013 cChannelCamRelation *ccr = GetEntry(ChannelID);
3014 return ccr ? ccr->CamChecked(CamSlotNumber) : false;
3015}
3016
3017bool cChannelCamRelations::CamDecrypt(tChannelID ChannelID, int CamSlotNumber)
3018{
3019 cMutexLock MutexLock(&mutex);
3020 cChannelCamRelation *ccr = GetEntry(ChannelID);
3021 return ccr ? ccr->CamDecrypt(CamSlotNumber) : false;
3022}
3023
3024void cChannelCamRelations::SetChecked(tChannelID ChannelID, int CamSlotNumber)
3025{
3026 cMutexLock MutexLock(&mutex);
3027 cChannelCamRelation *ccr = AddEntry(ChannelID);
3028 if (ccr)
3029 ccr->SetChecked(CamSlotNumber);
3030}
3031
3032void cChannelCamRelations::SetDecrypt(tChannelID ChannelID, int CamSlotNumber)
3033{
3034 cMutexLock MutexLock(&mutex);
3035 cChannelCamRelation *ccr = AddEntry(ChannelID);
3036 if (ccr)
3037 ccr->SetDecrypt(CamSlotNumber);
3038}
3039
3040void cChannelCamRelations::ClrChecked(tChannelID ChannelID, int CamSlotNumber)
3041{
3042 cMutexLock MutexLock(&mutex);
3043 cChannelCamRelation *ccr = GetEntry(ChannelID);
3044 if (ccr)
3045 ccr->ClrChecked(CamSlotNumber);
3046}
3047
3048void cChannelCamRelations::ClrDecrypt(tChannelID ChannelID, int CamSlotNumber)
3049{
3050 cMutexLock MutexLock(&mutex);
3051 cChannelCamRelation *ccr = GetEntry(ChannelID);
3052 if (ccr)
3053 ccr->ClrDecrypt(CamSlotNumber);
3054}
3055
3056void cChannelCamRelations::Load(const char *FileName)
3057{
3058 cMutexLock MutexLock(&mutex);
3059 fileName = FileName;
3060 if (access(fileName, R_OK) == 0) {
3061 dsyslog("loading %s", *fileName);
3062 if (FILE *f = fopen(fileName, "r")) {
3063 cReadLine ReadLine;
3064 char *s;
3065 while ((s = ReadLine.Read(f)) != NULL) {
3066 if (char *p = strchr(s, ' ')) {
3067 *p = 0;
3068 if (*++p) {
3069 tChannelID ChannelID = tChannelID::FromString(s);
3070 if (ChannelID.Valid()) {
3071 char *q;
3072 char *strtok_next;
3073 while ((q = strtok_r(p, " ", &strtok_next)) != NULL) {
3074 int CamSlotNumber = atoi(q);
3075 if (CamSlotNumber >= 1 && CamSlotNumber <= MAX_CAM_NUMBER)
3076 SetDecrypt(ChannelID, CamSlotNumber);
3077 p = NULL;
3078 }
3079 }
3080 }
3081 }
3082 }
3083 fclose(f);
3084 }
3085 else
3087 }
3088}
3089
3091{
3092 if (!*fileName)
3093 return;
3094 cMutexLock MutexLock(&mutex);
3095 struct stat st;
3096 if (stat(fileName, &st) == 0) {
3097 if ((st.st_mode & S_IWUSR) == 0) {
3098 dsyslog("not saving %s (file is read-only)", *fileName);
3099 return;
3100 }
3101 }
3102 dsyslog("saving %s", *fileName);
3104 if (f.Open()) {
3105 for (cChannelCamRelation *ccr = First(); ccr; ccr = Next(ccr)) {
3106 if (ccr->ChannelID().Valid()) {
3107 cString s;
3108 for (int i = 1; i <= MAX_CAM_NUMBER; i++) {
3109 if (ccr->CamDecrypt(i))
3110 s = cString::sprintf("%s%s%d", *s ? *s : "", *s ? " " : "", i);
3111 }
3112 if (*s)
3113 fprintf(f, "%s %s\n", *ccr->ChannelID().ToString(), *s);
3114 }
3115 }
3116 f.Close();
3117 }
3118 else
3120}
#define CA_ENCRYPTED_MIN
Definition channels.h:44
#define LOCK_CHANNELS_READ
Definition channels.h:277
static bool DumpTPDUDataTransfer
Definition ci.c:31
#define ST_CREATE_SESSION_RESPONSE
Definition ci.c:658
#define T_DATA_MORE
Definition ci.c:503
#define AOT_PROFILE_CHANGE
Definition ci.c:681
#define AOT_APPLICATION_INFO_ENQ
Definition ci.c:682
cChannelCamRelations ChannelCamRelations
Definition ci.c:2960
#define AOT_ENTER_MENU
Definition ci.c:684
#define ST_OPEN_SESSION_REQUEST
Definition ci.c:655
#define QUERY_REPLY_TIMEOUT
Definition ci.c:1107
#define AOT_ENQ
Definition ci.c:702
#define AI_ANSWER
Definition ci.c:1403
#define CPLM_ONLY
Definition ci.c:924
#define MAX_CAM_NUMBER
Definition ci.c:2957
#define DEC2BCD(d)
#define AOT_PROFILE_ENQ
Definition ci.c:679
#define SS_NOT_ALLOCATED
Definition ci.c:665
#define CRA_DISCARD
Definition ci.c:360
#define DRI_MMI_MODE_ACK
Definition ci.c:1387
static char * GetString(int &Length, const uint8_t **Data)
Definition ci.c:96
cCamSlots CamSlots
Definition ci.c:2851
#define QUERY_REPLY_WAIT
Definition ci.c:2751
#define MM_HIGH_LEVEL
Definition ci.c:1381
#define MAX_TPDU_SIZE
Definition ci.c:488
#define AOT_APPLICATION_INFO
Definition ci.c:683
cCiResourceHandlers CiResourceHandlers
Definition ci.c:1777
static int MtdMapCaDescriptors(uchar *p, cMtdMapper *MtdMapper)
Definition ci.c:1031
#define T_CREATE_TC
Definition ci.c:495
static bool DumpDateTime
Definition ci.c:34
#define SIZE_INDICATOR
Definition ci.c:40
#define ST_CLOSE_SESSION_REQUEST
Definition ci.c:659
#define RI_DATE_TIME
Definition ci.c:673
#define SS_OK
Definition ci.c:664
#define AOT_TEXT_LAST
Definition ci.c:698
#define TC_ALIVE_TIMEOUT
Definition ci.c:1809
#define MAXCASYSTEMIDS
Definition ci.c:917
#define MODULE_RESET_TIMEOUT
Definition ci.c:2176
static uint8_t * SetLength(uint8_t *Data, int Length)
Definition ci.c:57
#define RI_MMI
Definition ci.c:674
#define T_DTC_REPLY
Definition ci.c:498
#define AOT_CLOSE_MMI
Definition ci.c:695
#define AOT_TUNE
Definition ci.c:689
#define AOT_LIST_LAST
Definition ci.c:707
#define CPCI_OK_DESCRAMBLING
Definition ci.c:930
#define CPCI_QUERY
Definition ci.c:932
#define STREAM_TYPE_VIDEO
Definition ci.c:2729
#define ST_CLOSE_SESSION_RESPONSE
Definition ci.c:660
#define T_TC_ERROR
Definition ci.c:501
#define EF_BLIND
Definition ci.c:1398
#define AOT_MENU_LAST
Definition ci.c:704
#define QUERY_WAIT_TIME
Definition ci.c:1106
#define AOT_DATE_TIME
Definition ci.c:694
#define CPCI_NOT_SELECTED
Definition ci.c:933
#define STREAM_TYPE_AUDIO
Definition ci.c:2730
#define CA_ENABLE(x)
Definition ci.c:1104
#define RI_RESOURCE_MANAGER
Definition ci.c:669
#define T_REQUEST_TC
Definition ci.c:499
#define DATA_INDICATOR
Definition ci.c:491
#define MAX_DUMP
#define T_CTC_REPLY
Definition ci.c:496
#define CRA_NONE
Definition ci.c:359
#define RI_CONDITIONAL_ACCESS_SUPPORT
Definition ci.c:671
#define AOT_ANSW
Definition ci.c:703
#define T_DELETE_TC
Definition ci.c:497
#define CHANNEL_CAM_RELATIONS_CLEANUP_INTERVAL
Definition ci.c:2958
#define AOT_PROFILE
Definition ci.c:680
#define AOT_CA_INFO_ENQ
Definition ci.c:685
static bool DebugProtocol
Definition ci.c:32
#define AI_CANCEL
Definition ci.c:1402
#define CPLM_ADD
Definition ci.c:925
#define MAX_TPDU_DATA
Definition ci.c:489
#define AOT_CA_INFO
Definition ci.c:686
#define T_RCV
Definition ci.c:494
#define CAM_CHECKED_TIMEOUT
Definition ci.c:2884
#define T_DATA_LAST
Definition ci.c:502
#define RI_APPLICATION_INFORMATION
Definition ci.c:670
#define RI_HOST_CONTROL
Definition ci.c:672
static int MtdMapStreams(uchar *p, cMtdMapper *MtdMapper, int Length)
Definition ci.c:1058
static int MtdMapCaDescriptor(uchar *p, cMtdMapper *MtdMapper)
Definition ci.c:1014
#define ST_OPEN_SESSION_RESPONSE
Definition ci.c:656
#define AOT_DISPLAY_CONTROL
Definition ci.c:696
#define CAEI_POSSIBLE
Definition ci.c:1096
#define RESOURCE_CLASS_MASK
Definition ci.c:727
#define AOT_CA_PMT
Definition ci.c:687
#define TC_POLL_TIMEOUT
Definition ci.c:1808
#define CRA_CONFIRM
Definition ci.c:361
static char * CopyString(int Length, const uint8_t *Data)
Definition ci.c:77
static bool DumpPolls
Definition ci.c:33
#define UNSCRAMBLE_TIME
Definition ci.c:308
#define CRA_SELECT
Definition ci.c:362
cCamResponses CamResponses
Definition ci.c:479
#define STREAM_TYPE_PRIVATE
Definition ci.c:2731
#define dbgprotocol(a...)
Definition ci.c:36
#define CPLM_UPDATE
Definition ci.c:926
#define AOT_CLEAR_REPLACE
Definition ci.c:691
#define DCC_SET_MMI_MODE
Definition ci.c:1373
#define AOT_CA_PMT_REPLY
Definition ci.c:688
#define AOT_NONE
Definition ci.c:678
#define AOT_DATE_TIME_ENQ
Definition ci.c:693
#define TS_PACKET_FACTOR
Definition ci.c:309
#define T_SB
Definition ci.c:493
#define T_NEW_TC
Definition ci.c:500
static const uint8_t * GetLength(const uint8_t *Data, int &Length)
Definition ci.c:42
#define MAX_SESSIONS_PER_TC
Definition ci.c:609
static int MtdMapStream(uchar *p, cMtdMapper *MtdMapper)
Definition ci.c:1048
#define AOT_REPLACE
Definition ci.c:690
bool CamResponsesLoad(const char *FileName, bool AllowComments, bool MustExist)
Definition ci.c:481
#define MODULE_CHECK_INTERVAL
Definition ci.c:2175
#define QUERY_RETRIES
Definition ci.c:1108
#define AOT_MENU_ANSW
Definition ci.c:706
#define AOT_DISPLAY_REPLY
Definition ci.c:697
#define CAT_MAXPACKETS
Definition ci.c:123
#define ST_SESSION_NUMBER
Definition ci.c:654
cChannelCamRelations ChannelCamRelations
Definition ci.c:2960
eModuleStatus
Definition ci.h:171
@ msReady
Definition ci.h:171
@ msPresent
Definition ci.h:171
@ msNone
Definition ci.h:171
@ msReset
Definition ci.h:171
cCamSlots CamSlots
Definition ci.c:2851
#define MAX_CAM_SLOTS_PER_ADAPTER
Definition ci.h:21
#define MAX_CONNECTIONS_PER_CAM_SLOT
Definition ci.h:22
static u_int32_t crc32(const char *d, int len, u_int32_t CRCvalue)
Definition util.c:267
virtual ~cCaActivationReceiver() override
Definition ci.c:331
cCaActivationReceiver(const cChannel *Channel, cCamSlot *CamSlot)
Definition ci.c:323
virtual void Receive(const uchar *Data, int Length) override
This function is called from the cDevice we are attached to, and delivers one TS packet from the set ...
Definition ci.c:336
time_t lastScrambledTime
Definition ci.c:314
cCamSlot * camSlot
Definition ci.c:313
bool handlingPid
Definition ci.c:128
virtual ~cCaPidReceiver() override
Definition ci.c:133
cMutex mutex
Definition ci.c:127
void Reset(void)
Definition ci.c:136
void DelEmmPids(void)
Definition ci.c:174
uchar mtdCatBuffer[CAT_MAXPACKETS][TS_SIZE]
Definition ci.c:124
virtual void Receive(const uchar *Data, int Length) override
This function is called from the cDevice we are attached to, and delivers one TS packet from the set ...
Definition ci.c:184
cCaPidReceiver(void)
Definition ci.c:148
uchar buffer[1024]
Definition ci.c:121
bool HandlingPid(void)
The cCaPidReceiver adds/deletes PIDs to/from the base class cReceiver, which in turn does the same on...
Definition ci.c:297
int catVersion
Definition ci.c:119
int mtdNumCatPackets
Definition ci.c:125
int length
Definition ci.c:126
uchar * bufp
Definition ci.c:122
bool HasCaPids(void) const
Definition ci.c:135
cVector< int > emmPids
Definition ci.c:120
void AddEmmPid(int Pid)
Definition ci.c:161
cCamResponse(void)
Definition ci.c:376
~cCamResponse()
Definition ci.c:383
char * text
Definition ci.c:367
int action
Definition ci.c:368
bool Parse(const char *s)
Definition ci.c:388
int camNumber
Definition ci.c:366
int Matches(int CamNumber, const char *Text) const
Definition ci.c:451
int GetMatch(int CamNumber, const char *Text) const
Definition ci.c:467
Definition ci.h:233
bool Devices(cVector< int > &DeviceNumbers)
Adds the numbers of any devices that currently use this CAM to the given DeviceNumbers.
Definition ci.c:2275
virtual bool RepliesToQuery(void)
Returns true if the CAM in this slot replies to queries and thus supports MCD ("Multi Channel Decrypt...
Definition ci.c:2533
cMutex assignMutex
Definition ci.h:240
virtual bool IsDecrypting(void)
Returns true if the CAM in this slot is currently used for decrypting.
Definition ci.c:2808
virtual bool Ready(void)
Returns 'true' if the CAM in this slot is ready to decrypt.
Definition ci.c:2464
virtual void InjectEit(int Sid)
Injects a generated EIT with a "present event" for the given Sid into the TS data stream sent to the ...
Definition ci.c:2843
int Priority(void)
Returns the priority of the device this slot is currently assigned to, or IDLEPRIORITY if it is not a...
Definition ci.c:2669
cCamSlot * MasterSlot(void)
Returns this CAM slot's master slot, or a pointer to itself if it is a master slot.
Definition ci.h:311
cMutex mutex
Definition ci.h:239
virtual const char * GetCamName(void)
Returns the name of the CAM in this slot, or NULL if there is no ready CAM in this slot.
Definition ci.c:2458
void DeleteAllConnections(void)
Definition ci.c:2298
int source
Definition ci.h:254
friend class cMtdCamSlot
Definition ci.h:237
int slotIndex
Definition ci.h:247
cCiSession * GetSessionByResourceId(uint32_t ResourceId)
Definition ci.c:2373
virtual bool CanDecrypt(const cChannel *Channel, cMtdMapper *MtdMapper=NULL)
Returns true if there is a CAM in this slot that is able to decrypt the given Channel (or at least cl...
Definition ci.c:2753
void KeepSharedCaPids(int ProgramNumber, const int *CaSystemIds, int *CaPids)
Definition ci.c:2579
virtual bool Inject(uchar *Data, int Count)
Sends all Count bytes of the given Data to the CAM, and returns true if this was possible.
Definition ci.c:2838
void MtdEnable(void)
Enables MTD support for this CAM.
Definition ci.c:2635
bool McdAvailable(void)
Returns true if this CAM supports MCD ("Multi Channel Decyption").
Definition ci.h:286
virtual bool EnterMenu(void)
Requests the CAM in this slot to start its menu.
Definition ci.c:2481
virtual uchar * Decrypt(uchar *Data, int &Count)
If this is a CAM slot that can be freely assigned to any device, but will not be directly inserted in...
Definition ci.c:2826
virtual void AddPid(int ProgramNumber, int Pid, int StreamType)
Adds the given PID information to the list of PIDs.
Definition ci.c:2692
int MasterSlotNumber(void)
Returns the number of this CAM's master slot within the whole system.
Definition ci.h:349
virtual cCiEnquiry * GetEnquiry(void)
Gets a pending enquiry, or NULL if there is no enquiry.
Definition ci.c:2501
bool mtdAvailable
Definition ci.h:257
void Process(cTPDU *TPDU=NULL)
Definition ci.c:2307
cMtdHandler * mtdHandler
Definition ci.h:258
cCaActivationReceiver * caActivationReceiver
Definition ci.h:246
virtual eModuleStatus ModuleStatus(void)
Returns the status of the CAM in this slot.
Definition ci.c:2444
void MtdActivate(bool On)
Activates (On == true) or deactivates (On == false) MTD.
Definition ci.c:2640
eModuleStatus lastModuleStatus
Definition ci.h:250
virtual ~cCamSlot() override
Definition ci.c:2203
cCaPidReceiver * caPidReceiver
Definition ci.h:245
virtual void AddChannel(const cChannel *Channel)
Adds all PIDs of the given Channel to the current list of PIDs.
Definition ci.c:2733
void NewConnection(void)
Definition ci.c:2285
bool resendPmt
Definition ci.h:253
int transponder
Definition ci.h:255
virtual bool HasUserIO(void)
Returns true if there is a pending user interaction, which shall be retrieved via GetMenu() or GetEnq...
Definition ci.c:2475
virtual const int * GetCaSystemIds(void)
Definition ci.c:2662
time_t resetTime
Definition ci.h:251
bool WantsTsData(void) const
Returns true if this CAM slot wants to receive the TS data through its Decrypt() function.
Definition ci.h:340
std::atomic< cDevice * > assignedDevice
Definition ci.h:244
virtual bool Assign(cDevice *Device, bool Query=false)
Assigns this CAM slot to the given Device, if this is possible.
Definition ci.c:2221
virtual cCiMenu * GetMenu(void)
Gets a pending menu, or NULL if there is no menu.
Definition ci.c:2488
cTimeMs moduleCheckTimer
Definition ci.h:252
virtual void SendCaPmt(uint8_t CmdId)
Definition ci.c:2627
cDevice * Device(void)
Returns the device this CAM slot is currently assigned to.
Definition ci.h:334
cCondVar processed
Definition ci.h:241
virtual bool Reset(void)
Resets the CAM in this slot.
Definition ci.c:2388
int slotNumber
Definition ci.h:248
virtual void SetPid(int Pid, bool Active)
Sets the given Pid (which has previously been added through a call to AddPid()) to Active.
Definition ci.c:2710
virtual void StartActivation(void)
Puts the CAM in this slot into a mode where an inserted smart card can be activated.
Definition ci.c:2411
virtual void StartDecrypting(void)
Sends all CA_PMT entries to the CAM that have been modified since the last call to this function.
Definition ci.c:2789
virtual bool IsActivating(void)
Returns true if this CAM slot is currently activating a smart card.
Definition ci.c:2437
friend class cCiConditionalAccessSupport
Definition ci.h:236
bool MtdAvailable(void)
Returns true if this CAM supports MTD ("Multi Transponder Decryption").
Definition ci.h:288
cCiTransportConnection * tc[MAX_CONNECTIONS_PER_CAM_SLOT+1]
Definition ci.h:249
virtual bool TsPostProcess(uchar *Data)
If there is a cCiSession that needs to do additional processing on TS packets (after the CAM has done...
Definition ci.c:2833
int MtdPutData(uchar *Data, int Count)
Sends at most Count bytes of the given Data to the individual MTD CAM slots that are using this CAM.
Definition ci.c:2657
void Write(cTPDU *TPDU)
Definition ci.c:2379
int SlotIndex(void)
Returns the index of this CAM slot within its CI adapter.
Definition ci.h:343
cCiAdapter * ciAdapter
Definition ci.h:242
cList< cCiCaProgramData > caProgramList
Definition ci.h:256
virtual void StopDecrypting(void)
Clears the list of CA_PMT entries and tells the CAM to stop decrypting.
Definition ci.c:2794
void SendCaPmts(cCiCaPmtList &CaPmtList)
Sends the given list of CA_PMTs to the CAM.
Definition ci.c:2616
virtual bool HasMMI(void)
Returns 'true' if the CAM in this slot has an active MMI.
Definition ci.c:2470
virtual bool CanActivate(void)
Returns true if there is a CAM in this slot that can be put into activation mode.
Definition ci.c:2406
cCamSlot(cCiAdapter *CiAdapter, bool WantsTsData=false, cCamSlot *MasterSlot=NULL)
Creates a new CAM slot for the given CiAdapter.
Definition ci.c:2178
cCamSlot * MtdSpawn(void)
If this CAM slot can do MTD ("Multi Transponder Decryption"), a call to this function returns a cMtdC...
Definition ci.c:2213
cCamSlot * masterSlot
Definition ci.h:243
void BuildCaPmts(uint8_t CmdId, cCiCaPmtList &CaPmtList, cMtdMapper *MtdMapper=NULL)
Generates all CA_PMTs with the given CmdId and stores them in the given CaPmtList.
Definition ci.c:2540
int SlotNumber(void)
Returns the number of this CAM slot within the whole system.
Definition ci.h:346
friend class cCiAdapter
Definition ci.h:234
virtual void CancelActivation(void)
Cancels a previously started activation (if any).
Definition ci.c:2426
virtual bool ProvidesCa(const int *CaSystemIds)
Returns true if the CAM in this slot provides one of the given CaSystemIds.
Definition ci.c:2677
bool WaitForAllCamSlotsReady(int Timeout=0)
Waits until all CAM slots have become ready, or the given Timeout (seconds) has expired.
Definition ci.c:2863
int NumReadyMasterSlots(void)
Returns the number of master CAM slots in the system that are ready to decrypt.
Definition ci.c:2853
bool CamChecked(int CamSlotNumber)
Definition ci.c:2917
void ClrChecked(int CamSlotNumber)
Definition ci.c:2944
bool TimedOut(void)
Definition ci.c:2912
time_t lastChecked
Definition ci.c:2891
uint32_t camSlotsDecrypt
Definition ci.c:2890
tChannelID ChannelID(void)
Definition ci.c:2895
void SetChecked(int CamSlotNumber)
Definition ci.c:2931
cChannelCamRelation(tChannelID ChannelID)
Definition ci.c:2904
void SetDecrypt(int CamSlotNumber)
Definition ci.c:2938
bool CamDecrypt(int CamSlotNumber)
Definition ci.c:2926
uint32_t camSlotsChecked
Definition ci.c:2889
tChannelID channelID
Definition ci.c:2888
void ClrDecrypt(int CamSlotNumber)
Definition ci.c:2950
void ClrDecrypt(tChannelID ChannelID, int CamSlotNumber)
Definition ci.c:3048
void Load(const char *FileName)
Definition ci.c:3056
void SetChecked(tChannelID ChannelID, int CamSlotNumber)
Definition ci.c:3024
bool CamDecrypt(tChannelID ChannelID, int CamSlotNumber)
Definition ci.c:3017
cChannelCamRelations(void)
Definition ci.c:2962
void Cleanup(void)
Definition ci.c:2967
bool CamChecked(tChannelID ChannelID, int CamSlotNumber)
Definition ci.c:3010
time_t lastCleanup
Definition ci.h:518
cChannelCamRelation * GetEntry(tChannelID ChannelID)
Definition ci.c:2981
cChannelCamRelation * AddEntry(tChannelID ChannelID)
Definition ci.c:2992
void SetDecrypt(tChannelID ChannelID, int CamSlotNumber)
Definition ci.c:3032
cString fileName
Definition ci.h:515
void ClrChecked(tChannelID ChannelID, int CamSlotNumber)
Definition ci.c:3040
void Save(void)
Definition ci.c:3090
void Reset(int CamSlotNumber)
Definition ci.c:3001
const int * Dpids(void) const
Definition channels.h:158
int Vpid(void) const
Definition channels.h:154
int Source(void) const
Definition channels.h:152
int Ca(int Index=0) const
Definition channels.h:181
const int * Apids(void) const
Definition channels.h:157
int Transponder(void) const
Returns the transponder frequency in MHz, plus the polarization in case of sat.
Definition channels.c:154
int Sid(void) const
Definition channels.h:184
const int * Spids(void) const
Definition channels.h:159
cCamSlot * camSlots[MAX_CAM_SLOTS_PER_ADAPTER]
Definition ci.h:176
virtual bool Reset(int Slot)
Resets the CAM in the given Slot.
Definition ci.h:195
cCiAdapter(void)
Definition ci.c:2115
virtual eModuleStatus ModuleStatus(int Slot)
Returns the status of the CAM in the given Slot.
Definition ci.h:198
cCamSlot * ItCamSlot(int &Iter)
Iterates over all added CAM slots of this adapter.
Definition ci.c:2143
virtual bool Assign(cDevice *Device, bool Query=false)
Assigns this adapter to the given Device, if this is possible.
Definition ci.h:200
virtual ~cCiAdapter() override
The derived class must call Cancel(3) in its destructor.
Definition ci.c:2122
friend class cCamSlot
Definition ci.h:174
void AddCamSlot(cCamSlot *CamSlot)
Adds the given CamSlot to this CI adapter.
Definition ci.c:2129
virtual void Action(void) override
Handles the attached CAM slots in a separate thread.
Definition ci.c:2154
virtual void Write(const uint8_t *Buffer, int Length)
Writes Length bytes of the given Buffer.
Definition ci.h:193
virtual int Read(uint8_t *Buffer, int MaxLength)
Reads one chunk of data into the given Buffer, up to MaxLength bytes.
Definition ci.h:188
const char * GetMenuString(void)
Definition ci.h:81
virtual ~cCiApplicationInformation() override
Definition ci.c:867
virtual void Process(int Length=0, const uint8_t *Data=NULL) override
Definition ci.c:872
uint16_t manufacturerCode
Definition ci.h:74
cCiApplicationInformation(uint16_t SessionId, cCiTransportConnection *Tc)
Definition ci.c:859
bool EnterMenu(void)
Definition ci.c:905
uint8_t applicationType
Definition ci.h:72
uint16_t applicationManufacturer
Definition ci.h:73
cCiCaPidData(int Pid, int StreamType)
Definition ci.c:2083
int pid
Definition ci.c:2081
int streamType
Definition ci.c:2082
bool active
Definition ci.c:2080
Definition ci.c:935
int transponder
Definition ci.c:943
int programNumber
Definition ci.c:944
int esInfoLengthPos
Definition ci.c:939
int caSystemIds[MAXCASYSTEMIDS+1]
Definition ci.c:945
cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds)
Definition ci.c:956
uint8_t CmdId(void)
Definition ci.c:949
void AddPid(int Pid, uint8_t StreamType)
Definition ci.c:984
void SetListManagement(uint8_t ListManagement)
Definition ci.c:979
cDynamicBuffer capmt
Definition ci.c:941
cDynamicBuffer caDescriptors
Definition ci.c:940
int source
Definition ci.c:942
friend class cCiConditionalAccessSupport
Definition ci.c:936
void MtdMapPids(cMtdMapper *MtdMapper)
Definition ci.c:1073
void AddCaDescriptors(int Length, const uint8_t *Data)
Definition ci.c:998
uint8_t cmdId
Definition ci.c:938
uint8_t ListManagement(void)
Definition ci.c:951
int programNumber
Definition ci.c:2095
bool modified
Definition ci.c:2096
cList< cCiCaPidData > pidList
Definition ci.c:2097
cCiCaProgramData(int ProgramNumber)
Definition ci.c:2098
bool Active(void)
Definition ci.c:2103
bool RepliesToQuery(void)
Definition ci.c:1123
bool CanDecrypt(void)
Definition ci.c:1126
int caSystemIds[MAXCASYSTEMIDS+1]
Definition ci.c:1114
cCiConditionalAccessSupport(uint16_t SessionId, cCiTransportConnection *Tc)
Definition ci.c:1129
bool ReceivedReply(void)
Definition ci.c:1125
const int * GetCaSystemIds(void)
Definition ci.c:1121
virtual void Process(int Length=0, const uint8_t *Data=NULL) override
Definition ci.c:1139
void SendPMT(cCiCaPmt *CaPmt)
Definition ci.c:1257
int interval
Definition ci.c:1303
time_t lastTime
Definition ci.c:1304
void SendDateTime(void)
Definition ci.c:1319
virtual void Process(int Length=0, const uint8_t *Data=NULL) override
Definition ci.c:1344
cCiDateTime(uint16_t SessionId, cCiTransportConnection *Tc)
Definition ci.c:1311
virtual const uint32_t * ResourceIds(void) const override
Returns a pointer to an array of resource identifiers, where the last value is zero.
Definition ci.c:1748
virtual cCiSession * GetNewCiSession(uint32_t ResourceId, uint16_t SessionId, cCiTransportConnection *Tc) override
Returns a new cCiSession, according to the given ResourceId.
Definition ci.c:1762
friend class cCiMMI
Definition ci.h:151
void Cancel(void)
Definition ci.c:1718
cCiEnquiry(cCiMMI *MMI)
Definition ci.c:1694
~cCiEnquiry()
Definition ci.c:1703
cCiMMI * mmi
Definition ci.h:153
char * text
Definition ci.h:155
cMutex * mutex
Definition ci.h:154
void Abort(void)
Definition ci.c:1723
void Reply(const char *s)
Definition ci.c:1711
bool blind
Definition ci.h:156
int expectedLength
Definition ci.h:157
cCiHostControl(uint16_t SessionId, cCiTransportConnection *Tc)
Definition ci.c:1274
virtual void Process(int Length=0, const uint8_t *Data=NULL) override
Definition ci.c:1280
Definition ci.c:1405
cCiEnquiry * Enquiry(bool Clear=false)
Definition ci.c:1596
cCiMenu * Menu(bool Clear=false)
Definition ci.c:1585
cCiEnquiry * enquiry
Definition ci.c:1409
cCiMMI(uint16_t SessionId, cCiTransportConnection *Tc)
Definition ci.c:1422
cCiMenu * menu
Definition ci.c:1408
cCiEnquiry * fetchedEnquiry
Definition ci.c:1409
bool SendCloseMMI(void)
Definition ci.c:1628
virtual ~cCiMMI() override
Definition ci.c:1430
bool SendAnswer(const char *Text)
Definition ci.c:1613
char * GetText(int &Length, const uint8_t **Data)
Definition ci.c:1444
void SendMenuAnswer(uint8_t Selection)
Definition ci.c:1607
virtual void Process(int Length=0, const uint8_t *Data=NULL) override
Definition ci.c:1460
virtual bool HasUserIO(void) override
Definition ci.c:1414
cCiMenu * fetchedMenu
Definition ci.c:1408
Definition ci.h:120
bool selectable
Definition ci.h:127
void Abort(void)
Definition ci.c:1685
char * bottomText
Definition ci.h:130
bool HasUpdate(void)
Definition ci.c:1667
friend class cCiMMI
Definition ci.h:122
char * subTitleText
Definition ci.h:129
cMutex * mutex
Definition ci.h:126
void Cancel(void)
Definition ci.c:1680
bool Selectable(void)
Definition ci.h:142
int numEntries
Definition ci.h:132
bool AddEntry(char *s)
Definition ci.c:1658
cCiMenu(cCiMMI *MMI, bool Selectable)
Definition ci.c:1637
~cCiMenu()
Definition ci.c:1646
void Select(int Index)
Definition ci.c:1673
char * titleText
Definition ci.h:128
@ MAX_CIMENU_ENTRIES
Definition ci.h:124
cCiMMI * mmi
Definition ci.h:125
char * entries[MAX_CIMENU_ENTRIES]
Definition ci.h:131
cCiResourceHandler(void)
Creates a new resource handler, through which the available resources can be provides.
Definition ci.c:1732
virtual ~cCiResourceHandler() override
Definition ci.c:1736
virtual const uint32_t * ResourceIds(void) const =0
Returns a pointer to an array of resource identifiers, where the last value is zero.
cCiResourceHandlers(void)
Creates the default list of resourceIds.
Definition ci.c:1779
int NumIds(void)
Definition ci.h:112
cVector< uint32_t > resourceIds
Definition ci.h:102
const uint32_t * Ids(void)
Definition ci.h:111
void Register(cCiResourceHandler *ResourceHandler)
Adds the given ResourceHandler to the list of resource handlers and appends its ResourceIds to the gl...
Definition ci.c:1784
cCiSession * GetNewCiSession(uint32_t ResourceId, uint16_t SessionId, cCiTransportConnection *Tc)
Definition ci.c:1797
cCiResourceManager(uint16_t SessionId, cCiTransportConnection *Tc)
Definition ci.c:812
virtual void Process(int Length=0, const uint8_t *Data=NULL) override
Definition ci.c:819
uint16_t SessionId(void)
Definition ci.h:53
int GetTag(int &Length, const uint8_t **Data)
Definition ci.c:750
void SendData(int Tag, int Length=0, const uint8_t *Data=NULL)
Definition ci.c:771
cCiTransportConnection * tc
Definition ci.h:37
uint32_t resourceId
Definition ci.h:36
cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc)
Definition ci.c:729
void SetResourceId(uint32_t Id)
If this is a class that has been derived from an existing cCiSession class, but implements a differen...
Definition ci.c:740
uint32_t ResourceId(void)
Definition ci.h:54
virtual bool TsPostProcess(uint8_t *TsPacket)
If this cCiSession needs to do additional processing on TS packets (after the CAM has done the decryp...
Definition ci.h:58
cCamSlot * CamSlot(void)
Definition ci.c:793
const uint8_t * GetData(const uint8_t *Data, int &Length)
Definition ci.c:765
void SetTsPostProcessor(void)
If this cCiSession implements the TsPostProcess() function, it shall call SetTsPostProcessor() to reg...
Definition ci.c:745
uint16_t sessionId
Definition ci.h:35
virtual void Process(int Length=0, const uint8_t *Data=NULL)
Definition ci.c:798
cCiTransportConnection * Tc(void)
Definition ci.h:49
virtual ~cCiSession()
Definition ci.c:736
virtual ~cCiTransportConnection()
Definition ci.c:1826
cCiSession * GetSessionBySessionId(uint16_t SessionId)
Definition ci.c:1904
void CreateConnection(void)
Definition ci.c:640
bool TsPostProcess(uint8_t *TsPacket)
Definition ci.c:1837
void OpenSession(int Length, const uint8_t *Data)
Definition ci.c:1923
bool createConnectionRequested
Definition ci.c:618
cCamSlot * CamSlot(void)
Definition ci.c:638
cCiSession * sessions[MAX_SESSIONS_PER_TC+1]
Definition ci.c:623
cCamSlot * camSlot
Definition ci.c:615
cCiSession * tsPostProcessor
Definition ci.c:624
bool Ready(void)
Definition ci.c:1845
void SetTsPostProcessor(cCiSession *CiSession)
Definition ci.c:1832
const char * GetCamName(void)
Definition ci.c:1851
void SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId=0, int Status=-1)
Definition ci.c:1871
cCiTransportConnection(cCamSlot *CamSlot, uint8_t Tcid)
Definition ci.c:1811
void Poll(void)
Definition ci.c:1889
void DeleteConnection(void)
Definition ci.c:641
void SendTPDU(uint8_t Tag, int Length=0, const uint8_t *Data=NULL)
Definition ci.c:1857
void CloseSession(uint16_t SessionId)
Definition ci.c:1946
void SendData(int Length, const uint8_t *Data)
Definition ci.c:1864
uint32_t ResourceIdToInt(const uint8_t *Data)
Definition ci.c:1899
bool deleteConnectionRequested
Definition ci.c:619
bool HasUserIO(void)
Definition ci.c:644
void HandleSessions(cTPDU *TPDU)
Definition ci.c:1961
bool Process(cTPDU *TPDU=NULL)
Definition ci.c:1988
uint8_t Tcid(void) const
Definition ci.c:639
cCiSession * GetSessionByResourceId(uint32_t ResourceId)
Definition ci.c:1909
bool TimedWait(cMutex &Mutex, int TimeoutMs)
Definition thread.c:135
void Broadcast(void)
Definition thread.c:155
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition thread.c:73
cConfig(const char *NeedsLocking=NULL)
Definition config.h:129
bool Load(const char *FileName=NULL, bool AllowComments=false, bool MustExist=false)
Definition config.h:132
int Priority(bool IgnoreOccupied=false) const
Returns the priority of the current receiving session (-MAXPRIORITY..MAXPRIORITY),...
Definition device.c:1807
void SetCamSlot(cCamSlot *CamSlot)
Sets the given CamSlot to be used with this device.
Definition device.c:468
int DeviceNumber(void) const
Returns the number of this device (0 ... numDevices - 1).
Definition device.c:168
bool AttachReceiver(cReceiver *Receiver)
Attaches the given receiver to this device.
Definition device.c:1927
static int CurrentChannel(void)
Returns the number of the current channel on the primary device.
Definition device.h:371
void Detach(cFilter *Filter)
Detaches the given filter from this device.
Definition device.c:740
cCamSlot * CamSlot(void) const
Returns the CAM slot that is currently used with this device, or NULL if no CAM slot is in use.
Definition device.h:493
void Set(int Index, uchar Data)
Definition tools.h:899
uchar Get(int Index)
Definition tools.h:900
int Length(void)
Definition tools.h:903
void Append(const uchar *Data, int Length)
Definition tools.c:2404
uchar * Data(void)
Definition tools.h:902
uchar * Data(void)
Definition remux.h:446
int Length(void)
Definition remux.h:447
virtual void Clear(void)
Definition tools.c:2305
void Del(cListObject *Object, bool DeleteObject=true)
Definition tools.c:2260
int Count(void) const
Definition tools.h:646
void Add(cListObject *Object, cListObject *After=NULL)
Definition tools.c:2228
cListObject(const cListObject &ListObject)
Definition tools.h:553
int Index(void) const
Definition tools.c:2148
cListObject * Next(void) const
Definition tools.h:566
Definition tools.h:650
const cCiResourceHandler * Prev(const cCiResourceHandler *Object) const
Definition tools.h:666
const cCamResponse * First(void) const
Definition tools.h:662
const cCamResponse * Next(const cCamResponse *Object) const
Definition tools.h:669
const cCiResourceHandler * Last(void) const
Definition tools.h:664
cMtdMapper * MtdMapper(void)
Definition mtd.h:170
int PutCat(const uchar *Data, int Count)
Definition mtd.c:367
int Put(const uchar *Data, int Count)
Puts at most Count bytes of Data into the CAM slot which's index is derived from the PID of the TS pa...
Definition mtd.c:60
bool IsActivating(void)
Returns true if any of the active MTD CAM slots is currently activating.
Definition mtd.c:129
void StartDecrypting(void)
Tells all active MTD CAM slots to start decrypting.
Definition mtd.c:105
bool IsDecrypting(void)
Returns true if any of the active MTD CAM slots is currently decrypting.
Definition mtd.c:96
void CancelActivation(void)
Tells all active MTD CAM slots to cancel activation.
Definition mtd.c:123
cMtdCamSlot * GetMtdCamSlot(cCamSlot *MasterSlot)
Creates a new MTD CAM slot, or reuses an existing one that is currently unused.
Definition mtd.c:46
void StopDecrypting(void)
Tells all active MTD CAM slots to stop decrypting.
Definition mtd.c:115
int Priority(void)
Returns the maximum priority of any of the active MTD CAM slots.
Definition mtd.c:88
void UnAssignAll(void)
Unassigns all MTD CAM slots from their devices.
Definition mtd.c:145
bool Devices(cVector< int > &DeviceNumbers)
Adds the numbers of the devices of any active MTD CAM slots to the given DeviceNumbers.
Definition mtd.c:138
char * Read(FILE *f)
Definition tools.c:1574
bool AddPids(const int *Pids)
Adds the given zero terminated list of Pids to the list of PIDs of this receiver.
Definition receiver.c:60
friend class cDevice
Definition receiver.h:19
void Detach(void)
Definition receiver.c:125
bool AddPid(int Pid)
Adds the given Pid to the list of PIDs of this receiver.
Definition receiver.c:42
void DelPids(const int *Pids)
Deletes the given zero terminated list of Pids from the list of PIDs of this receiver.
Definition receiver.c:106
int NumPids(void) const
Definition receiver.h:83
cReceiver(const cChannel *Channel=NULL, int Priority=MINPRIORITY)
Creates a new receiver for the given Channel with the given Priority.
Definition receiver.c:14
cDevice * Device(void)
Definition receiver.h:34
void DelPid(int Pid)
Deletes the given Pid from the list of PIDs of this receiver.
Definition receiver.c:90
bool Open(void)
Definition tools.c:1812
bool Close(void)
Definition tools.c:1822
int QueueMessage(eMessageType Type, const char *s, int Seconds=0, int Timeout=0)
Like Message(), but this function may be called from a background thread.
Definition skins.c:334
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition tools.c:1242
Definition ci.c:505
const uint8_t * Data(int &Length)
Definition ci.c:516
uint8_t Status(void)
Definition ci.c:600
int MaxSize(void)
Definition ci.c:521
void Dump(int SlotNumber, bool Outgoing)
Definition ci.c:571
uint8_t buffer[MAX_TPDU_SIZE]
Definition ci.c:508
int size
Definition ci.c:507
int Size(void)
Definition ci.c:519
uint8_t Tcid(void)
Definition ci.c:514
uint8_t Tag(void)
Definition ci.c:515
void SetSize(int Size)
Definition ci.c:520
uint8_t * Buffer(void)
Definition ci.c:518
uint8_t Slot(void)
Definition ci.c:513
cTPDU(void)
Definition ci.c:511
const uint8_t * GetData(const uint8_t *Data, int &Length)
Definition ci.c:588
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition thread.h:110
cThread(const char *Description=NULL, bool LowPriority=false)
Creates a new thread.
Definition thread.c:264
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
Definition thread.c:380
void Set(int Ms=0)
Sets the timer.
Definition tools.c:842
bool TimedOut(void) const
Returns true if the number of milliseconds given in the last call to Set() have passed.
Definition tools.c:850
int Size(void) const
Definition tools.h:773
virtual void Clear(void)
Definition tools.h:828
virtual void Append(T Data)
Definition tools.h:794
bool RemoveElement(const T &Data)
Definition tools.h:819
#define MINPRIORITY
Definition config.h:42
#define IDLEPRIORITY
Definition config.h:45
#define tr(s)
Definition i18n.h:85
void MtdMapPid(uchar *p, cMtdMapper *MtdMapper)
Definition mtd.c:241
void MtdMapSid(uchar *p, cMtdMapper *MtdMapper)
Definition mtd.c:233
@ TableIdCAT
Definition si.h:24
@ CaDescriptorTag
Definition si.h:59
int GetCaPids(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, int *Pids)
Gets all CA pids for a given channel.
Definition pat.c:273
void GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, cDynamicBuffer &Buffer, int EsPid)
Gets all CA descriptors for a given channel.
Definition pat.c:268
#define MAXRECEIVEPIDS
Definition receiver.h:16
int TsPid(const uchar *p)
Definition remux.h:82
#define CATPID
Definition remux.h:53
bool TsIsScrambled(const uchar *p)
Definition remux.h:93
#define TS_SIZE
Definition remux.h:34
bool TsPayloadStart(const uchar *p)
Definition remux.h:72
cSkins Skins
Definition skins.c:257
@ mtInfo
Definition skins.h:37
~cCiCaPmtList()
Definition ci.c:2514
void Del(cCiCaPmt *CaPmt)
Definition ci.c:2527
cVector< cCiCaPmt * > caPmts
Definition ci.h:227
cCiCaPmt * Add(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds)
Definition ci.c:2520
bool Valid(void) const
Definition channels.h:58
static tChannelID FromString(const char *s)
Definition channels.c:23
bool isnumber(const char *s)
Definition tools.c:402
T get_unaligned(T *p)
Definition tools.h:82
uint16_t Peek13(const uchar *p)
Definition tools.h:297
#define LOG_ERROR_STR(s)
Definition tools.h:40
unsigned char uchar
Definition tools.h:31
#define dsyslog(a...)
Definition tools.h:37
#define MALLOC(type, size)
Definition tools.h:47
char * skipspace(const char *s)
Definition tools.h:244
T min(T a, T b)
Definition tools.h:63
#define esyslog(a...)
Definition tools.h:35
void put_unaligned(unsigned int v, T *p)
Definition tools.h:88
#define isyslog(a...)
Definition tools.h:36