12 #include <linux/dvb/ca.h> 14 #include <netinet/in.h> 17 #include <sys/ioctl.h> 34 #define dbgprotocol(a...) do { if (DebugProtocol) fprintf(stderr, a); } while (0) 38 #define SIZE_INDICATOR 0x80 40 static const uint8_t *
GetLength(
const uint8_t *Data,
int &Length)
47 int l = Length & ~SIZE_INDICATOR;
49 for (
int i = 0; i < l; i++)
50 Length = (Length << 8) | *Data++;
55 static uint8_t *
SetLength(uint8_t *Data,
int Length)
63 int n =
sizeof(Length);
64 for (
int i = n - 1; i >= 0; i--) {
65 int b = (Length >> (8 * i)) & 0xFF;
81 while (Length > 0 && (*Data ==
' ' || *Data == 0x05 || *Data == 0x96 || *Data == 0x97)) {
85 char *s =
MALLOC(
char, Length + 1);
86 strncpy(s, (
char *)Data, Length);
93 static char *
GetString(
int &Length,
const uint8_t **Data)
98 if (Length > 0 && Data && *Data) {
102 Length -= d - *Data + l;
169 length = (int(Data[6] & 0x03) << 8) | Data[7];
171 int v = (Data[10] & 0x3E) >> 1;
173 if (Data[11] == 0 && Data[12] == 0) {
176 memcpy(
buffer, Data + 13, n);
186 dsyslog(
"multi table CAT section - unhandled!");
195 memcpy(
bufp, Data + 4, n);
204 esyslog(
"ERROR: buffer overflow in cCaPidReceiver::Receive()");
213 AttachedDevice->
Detach(
this);
215 for (
int i = 0; i <
length - 4; i++) {
217 int CaId = int(p[i + 2] << 8) | p[i + 3];
218 int EmmPid = int(((p[i + 4] & 0x1F) << 8)) | p[i + 5];
221 case 0x01:
for (
int j = i + 7; j < p[i + 1] + 2; j += 4) {
222 EmmPid = (int(p[j] & 0x0F) << 8) | p[j + 1];
227 i += p[i + 1] + 2 - 1;
245 #define UNSCRAMBLE_TIME 5 // seconds of receiving purely unscrambled data before considering the smart card "activated" 246 #define TS_PACKET_FACTOR 1024 // only process every TS_PACKET_FACTORth packet to keep the load down 276 time_t Now = time(NULL);
289 #define MAX_TPDU_SIZE 2048 290 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4) 292 #define DATA_INDICATOR 0x80 296 #define T_CREATE_TC 0x82 297 #define T_CTC_REPLY 0x83 298 #define T_DELETE_TC 0x84 299 #define T_DTC_REPLY 0x85 300 #define T_REQUEST_TC 0x86 301 #define T_NEW_TC 0x87 302 #define T_TC_ERROR 0x88 303 #define T_DATA_LAST 0xA0 304 #define T_DATA_MORE 0xA1 310 const uint8_t *GetData(
const uint8_t *Data,
int &Length);
313 cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag,
int Length = 0,
const uint8_t *Data = NULL);
314 uint8_t
Slot(
void) {
return buffer[0]; }
315 uint8_t
Tcid(
void) {
return buffer[1]; }
316 uint8_t
Tag(
void) {
return buffer[2]; }
317 const uint8_t *
Data(
int &Length) {
return GetData(buffer + 3, Length); }
318 uint8_t Status(
void);
320 int Size(
void) {
return size; }
323 void Dump(
int SlotNumber,
bool Outgoing);
326 cTPDU::cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag,
int Length,
const uint8_t *Data)
352 esyslog(
"ERROR: invalid data length for TPDU tag 0x%02X: %d (%d/%d)", Tag, Length, Slot, Tcid);
357 uint8_t *p = buffer + 3;
361 memcpy(p, Data, Length);
362 size = Length + (p - buffer);
365 esyslog(
"ERROR: invalid data length for TPDU tag 0x%02X: %d (%d/%d)", Tag, Length, Slot, Tcid);
368 esyslog(
"ERROR: unknown TPDU tag: 0x%02X (%d/%d)", Tag, Slot, Tcid);
376 fprintf(stderr,
" %d: %s ", SlotNumber, Outgoing ?
"-->" :
"<--");
377 for (
int i = 0; i < size && i <
MAX_DUMP; i++)
378 fprintf(stderr,
"%02X ", buffer[i]);
379 fprintf(stderr,
"%s\n", size >= MAX_DUMP ?
"..." :
"");
381 fprintf(stderr,
" ");
382 for (
int i = 0; i < size && i <
MAX_DUMP; i++)
383 fprintf(stderr,
"%2c ", isprint(buffer[i]) ? buffer[i] :
'.');
384 fprintf(stderr,
"%s\n", size >= MAX_DUMP ?
"..." :
"");
403 if (size >= 4 && buffer[size - 4] ==
T_SB && buffer[size - 3] == 2)
404 return buffer[size - 1];
410 #define MAX_SESSIONS_PER_TC 16 424 void SendTPDU(uint8_t Tag,
int Length = 0,
const uint8_t *Data = NULL);
425 void SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId = 0,
int Status = -1);
427 uint32_t ResourceIdToInt(
const uint8_t *Data);
428 cCiSession *GetSessionBySessionId(uint16_t SessionId);
429 void OpenSession(
int Length,
const uint8_t *Data);
430 void CloseSession(uint16_t SessionId);
431 void HandleSessions(
cTPDU *TPDU);
436 uint8_t
Tcid(
void)
const {
return tcid; }
439 const char *GetCamName(
void);
442 void SendData(
int Length,
const uint8_t *Data);
443 bool Process(
cTPDU *TPDU = NULL);
444 cCiSession *GetSessionByResourceId(uint32_t ResourceId);
451 #define ST_SESSION_NUMBER 0x90 452 #define ST_OPEN_SESSION_REQUEST 0x91 453 #define ST_OPEN_SESSION_RESPONSE 0x92 454 #define ST_CREATE_SESSION 0x93 455 #define ST_CREATE_SESSION_RESPONSE 0x94 456 #define ST_CLOSE_SESSION_REQUEST 0x95 457 #define ST_CLOSE_SESSION_RESPONSE 0x96 462 #define SS_NOT_ALLOCATED 0xF0 466 #define RI_RESOURCE_MANAGER 0x00010041 467 #define RI_APPLICATION_INFORMATION 0x00020041 468 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041 469 #define RI_HOST_CONTROL 0x00200041 470 #define RI_DATE_TIME 0x00240041 471 #define RI_MMI 0x00400041 475 #define AOT_NONE 0x000000 476 #define AOT_PROFILE_ENQ 0x9F8010 477 #define AOT_PROFILE 0x9F8011 478 #define AOT_PROFILE_CHANGE 0x9F8012 479 #define AOT_APPLICATION_INFO_ENQ 0x9F8020 480 #define AOT_APPLICATION_INFO 0x9F8021 481 #define AOT_ENTER_MENU 0x9F8022 482 #define AOT_CA_INFO_ENQ 0x9F8030 483 #define AOT_CA_INFO 0x9F8031 484 #define AOT_CA_PMT 0x9F8032 485 #define AOT_CA_PMT_REPLY 0x9F8033 486 #define AOT_TUNE 0x9F8400 487 #define AOT_REPLACE 0x9F8401 488 #define AOT_CLEAR_REPLACE 0x9F8402 489 #define AOT_ASK_RELEASE 0x9F8403 490 #define AOT_DATE_TIME_ENQ 0x9F8440 491 #define AOT_DATE_TIME 0x9F8441 492 #define AOT_CLOSE_MMI 0x9F8800 493 #define AOT_DISPLAY_CONTROL 0x9F8801 494 #define AOT_DISPLAY_REPLY 0x9F8802 495 #define AOT_TEXT_LAST 0x9F8803 496 #define AOT_TEXT_MORE 0x9F8804 497 #define AOT_KEYPAD_CONTROL 0x9F8805 498 #define AOT_KEYPRESS 0x9F8806 499 #define AOT_ENQ 0x9F8807 500 #define AOT_ANSW 0x9F8808 501 #define AOT_MENU_LAST 0x9F8809 502 #define AOT_MENU_MORE 0x9F880A 503 #define AOT_MENU_ANSW 0x9F880B 504 #define AOT_LIST_LAST 0x9F880C 505 #define AOT_LIST_MORE 0x9F880D 506 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E 507 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F 508 #define AOT_DISPLAY_MESSAGE 0x9F8810 509 #define AOT_SCENE_END_MARK 0x9F8811 510 #define AOT_SCENE_DONE 0x9F8812 511 #define AOT_SCENE_CONTROL 0x9F8813 512 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814 513 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815 514 #define AOT_FLUSH_DOWNLOAD 0x9F8816 515 #define AOT_DOWNLOAD_REPLY 0x9F8817 516 #define AOT_COMMS_CMD 0x9F8C00 517 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01 518 #define AOT_COMMS_REPLY 0x9F8C02 519 #define AOT_COMMS_SEND_LAST 0x9F8C03 520 #define AOT_COMMS_SEND_MORE 0x9F8C04 521 #define AOT_COMMS_RCV_LAST 0x9F8C05 522 #define AOT_COMMS_RCV_MORE 0x9F8C06 530 int GetTag(
int &Length,
const uint8_t **Data);
531 const uint8_t *GetData(
const uint8_t *Data,
int &Length);
532 void SendData(
int Tag,
int Length = 0,
const uint8_t *Data = NULL);
540 virtual void Process(
int Length = 0,
const uint8_t *Data = NULL);
545 sessionId = SessionId;
546 resourceId = ResourceId;
559 if (Length >= 3 && Data && *Data) {
561 for (
int i = 0; i < 3; i++)
562 t = (t << 8) | *(*Data)++;
572 return Length ? Data : NULL;
577 uint8_t buffer[2048];
581 *p++ = (sessionId >> 8) & 0xFF;
582 *p++ = sessionId & 0xFF;
583 *p++ = (Tag >> 16) & 0xFF;
584 *p++ = (Tag >> 8) & 0xFF;
587 if (p - buffer + Length <
int(
sizeof(buffer))) {
589 memcpy(p, Data, Length);
591 tc->SendData(p - buffer, buffer);
594 esyslog(
"ERROR: CAM %d: data length (%d) exceeds buffer size", Tc()->CamSlot()->SlotNumber(), Length);
608 virtual void Process(
int Length = 0,
const uint8_t *Data = NULL);
621 int Tag =
GetTag(Length, &Data);
640 const uint8_t *d =
GetData(Data, l);
642 esyslog(
"ERROR: CAM %d: resource manager: unexpected data",
Tc()->CamSlot()->SlotNumber());
648 esyslog(
"ERROR: CAM %d: resource manager: unexpected tag %06X in state %d",
Tc()->CamSlot()->SlotNumber(), Tag,
state);
652 default:
esyslog(
"ERROR: CAM %d: resource manager: unknown tag %06X",
Tc()->CamSlot()->SlotNumber(), Tag);
655 else if (
state == 0) {
674 virtual void Process(
int Length = 0,
const uint8_t *Data = NULL);
675 bool EnterMenu(
void);
695 int Tag =
GetTag(Length, &Data);
700 const uint8_t *d =
GetData(Data, l);
701 if ((l -= 1) < 0)
break;
703 if ((l -= 2) < 0)
break;
706 if ((l -= 2) < 0)
break;
715 default:
esyslog(
"ERROR: CAM %d: application information: unknown tag %06X",
Tc()->CamSlot()->SlotNumber(), Tag);
718 else if (
state == 0) {
737 #define MAXCASYSTEMIDS 64 741 #define CPLM_MORE 0x00 742 #define CPLM_FIRST 0x01 743 #define CPLM_LAST 0x02 744 #define CPLM_ONLY 0x03 745 #define CPLM_ADD 0x04 746 #define CPLM_UPDATE 0x05 750 #define CPCI_OK_DESCRAMBLING 0x01 751 #define CPCI_OK_MMI 0x02 752 #define CPCI_QUERY 0x03 753 #define CPCI_NOT_SELECTED 0x04 766 void AddCaDescriptors(
int Length,
const uint8_t *Data);
768 cCiCaPmt(uint8_t CmdId,
int Source,
int Transponder,
int ProgramNumber,
const int *CaSystemIds);
769 uint8_t
CmdId(
void) {
return cmdId; }
770 void SetListManagement(uint8_t ListManagement);
772 void AddPid(
int Pid, uint8_t StreamType);
775 cCiCaPmt::cCiCaPmt(uint8_t CmdId,
int Source,
int Transponder,
int ProgramNumber,
const int *CaSystemIds)
779 transponder = Transponder;
780 programNumber = ProgramNumber;
783 for (; CaSystemIds[i]; i++)
787 uint8_t caDescriptors[512];
788 int caDescriptorsLength =
GetCaDescriptors(source, transponder, programNumber,
caSystemIds,
sizeof(caDescriptors), caDescriptors, 0);
791 capmt[length++] = (ProgramNumber >> 8) & 0xFF;
792 capmt[length++] = ProgramNumber & 0xFF;
793 capmt[length++] = 0x01;
794 esInfoLengthPos = length;
795 capmt[length++] = 0x00;
796 capmt[length++] = 0x00;
797 AddCaDescriptors(caDescriptorsLength, caDescriptors);
802 capmt[0] = ListManagement;
808 uint8_t caDescriptors[512];
809 int caDescriptorsLength =
GetCaDescriptors(source, transponder, programNumber,
caSystemIds,
sizeof(caDescriptors), caDescriptors, Pid);
811 capmt[length++] = StreamType;
812 capmt[length++] = (Pid >> 8) & 0xFF;
813 capmt[length++] = Pid & 0xFF;
814 esInfoLengthPos = length;
815 capmt[length++] = 0x00;
816 capmt[length++] = 0x00;
817 AddCaDescriptors(caDescriptorsLength, caDescriptors);
823 if (esInfoLengthPos) {
824 if (length + Length <
int(
sizeof(capmt))) {
826 capmt[length++] = cmdId;
827 memcpy(capmt + length, Data, Length);
829 int l = length - esInfoLengthPos - 2;
830 capmt[esInfoLengthPos] = (l >> 8) & 0xFF;
831 capmt[esInfoLengthPos + 1] = l & 0xFF;
835 esyslog(
"ERROR: buffer overflow in CA descriptor");
839 esyslog(
"ERROR: adding CA descriptor without Pid!");
846 #define CAEI_POSSIBLE 0x01 847 #define CAEI_POSSIBLE_COND_PURCHASE 0x02 848 #define CAEI_POSSIBLE_COND_TECHNICAL 0x03 849 #define CAEI_NOT_POSSIBLE_ENTITLEMENT 0x71 850 #define CAEI_NOT_POSSIBLE_TECHNICAL 0x73 852 #define CA_ENABLE_FLAG 0x80 854 #define CA_ENABLE(x) (((x) & CA_ENABLE_FLAG) ? (x) & ~CA_ENABLE_FLAG : 0) 856 #define QUERY_WAIT_TIME 1000 // ms to wait before sending a query 857 #define QUERY_REPLY_TIMEOUT 2000 // ms to wait for a reply to a query 868 virtual void Process(
int Length = 0,
const uint8_t *Data = NULL);
872 bool Ready(
void) {
return state >= 4; }
889 int Tag =
GetTag(Length, &Data);
896 const uint8_t *d =
GetData(Data, l);
898 uint16_t
id = ((uint16_t)(*d) << 8) | *(d + 1);
906 esyslog(
"ERROR: CAM %d: too many CA system IDs!",
Tc()->CamSlot()->SlotNumber());
916 dsyslog(
"CAM %d: system ids:%s",
Tc()->CamSlot()->SlotNumber(), *Ids ? *Ids :
" none");
922 dsyslog(
"CAM %d: replies to QUERY - multi channel decryption possible",
Tc()->CamSlot()->SlotNumber());
927 const uint8_t *d =
GetData(Data, l);
929 uint16_t pnr = ((uint16_t)(*d) << 8) | *(d + 1);
938 if (l % 3 == 0 && l > 1) {
944 uint16_t len = ((uint16_t)(*d) << 8) | *(d + 1);
950 unsigned char caepl = *d;
958 uint16_t pid = ((uint16_t)(*d) << 8) | *(d + 1);
959 unsigned char caees = *(d + 2);
974 default:
esyslog(
"ERROR: CAM %d: conditional access support: unknown tag %06X",
Tc()->CamSlot()->SlotNumber(), Tag);
977 else if (
state == 0) {
989 dsyslog(
"CAM %d: doesn't reply to QUERY - only a single channel can be decrypted",
Tc()->CamSlot()->SlotNumber());
996 if (CaPmt &&
state >= 2) {
1009 void SendDateTime(
void);
1012 virtual void Process(
int Length = 0,
const uint8_t *Data = NULL);
1025 time_t t = time(NULL);
1028 if (gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc)) {
1029 int Y = tm_gmt.tm_year;
1030 int M = tm_gmt.tm_mon + 1;
1031 int D = tm_gmt.tm_mday;
1032 int L = (M == 1 || M == 2) ? 1 : 0;
1033 int MJD = 14956 + D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001);
1034 #define DEC2BCD(d) uint8_t(((d / 10) << 4) + (d % 10)) 1036 struct tTime { uint16_t mjd; uint8_t h, m, s;
short offset; };
1038 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)) };
1051 int Tag =
GetTag(Length, &Data);
1056 const uint8_t *d =
GetData(Data, l);
1064 default:
esyslog(
"ERROR: CAM %d: date time: unknown tag %06X",
Tc()->CamSlot()->SlotNumber(), Tag);
1077 #define DCC_SET_MMI_MODE 0x01 1078 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02 1079 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03 1080 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04 1081 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05 1085 #define MM_HIGH_LEVEL 0x01 1086 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02 1087 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03 1091 #define DRI_MMI_MODE_ACK 0x01 1092 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02 1093 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03 1094 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04 1095 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05 1096 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0 1097 #define DRI_UNKNOWN_MMI_MODE 0xF1 1098 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2 1102 #define EF_BLIND 0x01 1106 #define AI_CANCEL 0x00 1107 #define AI_ANSWER 0x01 1111 char *GetText(
int &Length,
const uint8_t **Data);
1117 virtual void Process(
int Length = 0,
const uint8_t *Data = NULL);
1119 cCiMenu *Menu(
bool Clear =
false);
1121 void SendMenuAnswer(uint8_t Selection);
1122 bool SendAnswer(
const char *Text);
1123 bool SendCloseMMI(
void);
1153 int Tag =
GetTag(Length, Data);
1160 esyslog(
"ERROR: CAM %d: MMI: unexpected text tag: %06X",
Tc()->CamSlot()->SlotNumber(), Tag);
1167 int Tag =
GetTag(Length, &Data);
1172 const uint8_t *d =
GetData(Data, l);
1177 struct tDisplayReply { uint8_t id; uint8_t mode; };
1183 default:
esyslog(
"ERROR: CAM %d: MMI: unsupported display control command %02X",
Tc()->CamSlot()->SlotNumber(), *d);
1194 const uint8_t *d =
GetData(Data, l);
1219 const uint8_t *d =
GetData(Data, l);
1221 uint8_t blind = *d++;
1236 const uint8_t *d =
GetData(Data, l);
1242 dbgprotocol(
"Slot %d: <== Close MMI (%d) id = %02X delay = %d\n",
Tc()->CamSlot()->SlotNumber(),
SessionId(),
id, delay);
1245 default:
esyslog(
"ERROR: CAM %d: MMI: unknown tag %06X",
Tc()->CamSlot()->SlotNumber(), Tag);
1281 struct tAnswer { uint8_t id;
char text[256]; };
1285 strncpy(answer.text, Text,
sizeof(answer.text));
1303 selectable = Selectable;
1304 titleText = subTitleText = bottomText = NULL;
1316 for (
int i = 0; i < numEntries; i++)
1322 if (numEntries < MAX_CIMENU_ENTRIES) {
1323 entries[numEntries++] = s;
1332 return !mmi || mmi->HasUserIO();
1338 if (mmi && -1 <= Index && Index < numEntries)
1339 mmi->SendMenuAnswer(Index + 1);
1351 mmi->SendCloseMMI();
1388 mmi->SendCloseMMI();
1393 #define TC_POLL_TIMEOUT 300 // ms WORKAROUND: TC_POLL_TIMEOUT < 300ms doesn't work with DragonCAM 1394 #define TC_ALIVE_TIMEOUT 2000 // ms after which a transport connection is assumed dead 1402 createConnectionRequested =
false;
1403 deleteConnectionRequested =
false;
1419 return cas && cas->
Ready();
1430 cTPDU TPDU(camSlot->SlotIndex(), tcid, Tag, Length, Data);
1431 camSlot->Write(&TPDU);
1438 if (state == stACTIVE && Length > 0)
1445 uint8_t *p = buffer;
1456 buffer[1] = p - buffer - 2;
1465 dbgprotocol(
"Slot %d: ==> Poll\n", camSlot->SlotNumber());
1483 if (sessions[i] && sessions[i]->
ResourceId() == ResourceId)
1491 if (Length == 6 && *(Data + 1) == 0x04) {
1492 uint32_t
ResourceId = ResourceIdToInt(Data + 2);
1494 if (!GetSessionByResourceId(ResourceId)) {
1497 switch (ResourceId) {
1504 default:
esyslog(
"ERROR: CAM %d: unknown resource identifier: %08X (%d/%d)", camSlot->SlotNumber(),
ResourceId, camSlot->SlotIndex(), tcid);
1511 esyslog(
"ERROR: CAM %d: no free session slot for resource identifier %08X (%d/%d)", camSlot->SlotNumber(),
ResourceId, camSlot->SlotIndex(), tcid);
1514 esyslog(
"ERROR: CAM %d: session for resource identifier %08X already exists (%d/%d)", camSlot->SlotNumber(),
ResourceId, camSlot->SlotIndex(), tcid);
1521 cCiSession *Session = GetSessionBySessionId(SessionId);
1522 if (Session && sessions[SessionId] == Session) {
1528 esyslog(
"ERROR: CAM %d: unknown session id: %d (%d/%d)", camSlot->SlotNumber(),
SessionId, camSlot->SlotIndex(), tcid);
1536 const uint8_t *Data = TPDU->
Data(Length);
1537 if (Data && Length > 1) {
1541 cCiSession *Session = GetSessionBySessionId(SessionId);
1543 Session->
Process(Length - 4, Data + 4);
1545 esyslog(
"ERROR: CAM %d: unknown session id: %d (%d/%d)", camSlot->SlotNumber(),
SessionId, camSlot->SlotIndex(), tcid);
1555 default:
esyslog(
"ERROR: CAM %d: unknown session tag: %02X (%d/%d)", camSlot->SlotNumber(), *Data, camSlot->SlotIndex(), tcid);
1564 else if (alive.TimedOut())
1568 if (createConnectionRequested) {
1569 dbgprotocol(
"Slot %d: create connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1570 createConnectionRequested =
false;
1577 dbgprotocol(
"Slot %d: connection created %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1581 else if (timer.TimedOut()) {
1582 dbgprotocol(
"Slot %d: timeout while creating connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1587 if (deleteConnectionRequested) {
1588 dbgprotocol(
"Slot %d: delete connection requested %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1589 deleteConnectionRequested =
false;
1595 switch (TPDU->
Tag()) {
1597 esyslog(
"ERROR: CAM %d: T_REQUEST_TC not implemented (%d/%d)", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1601 HandleSessions(TPDU);
1605 dbgprotocol(
"Slot %d: receive data %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1610 dbgprotocol(
"Slot %d: delete connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1622 esyslog(
"ERROR: unknown TPDU tag: 0x%02X (%s)", TPDU->
Tag(), __FUNCTION__);
1625 else if (timer.TimedOut())
1630 sessions[i]->Process();
1638 dbgprotocol(
"Slot %d: connection deleted %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid);
1643 esyslog(
"ERROR: unknown state: %d (%s)", state, __FUNCTION__);
1659 streamType = StreamType;
1672 programNumber = ProgramNumber;
1703 esyslog(
"ERROR: no free CAM slot in CI adapter");
1739 #define MODULE_CHECK_INTERVAL 500 // ms 1740 #define MODULE_RESET_TIMEOUT 2 // s 1744 ciAdapter = CiAdapter;
1745 assignedDevice = NULL;
1747 caActivationReceiver = NULL;
1752 source = transponder = 0;
1756 slotNumber = Index() + 1;
1758 ciAdapter->AddCamSlot(
this);
1765 assignedDevice->SetCamSlot(NULL);
1766 delete caPidReceiver;
1767 delete caActivationReceiver;
1769 DeleteAllConnections();
1776 if (ciAdapter->Assign(Device,
true)) {
1777 if (!Device && assignedDevice)
1778 assignedDevice->SetCamSlot(NULL);
1779 if (!Query || !Device) {
1781 source = transponder = 0;
1782 if (ciAdapter->Assign(Device)) {
1783 assignedDevice = Device;
1790 dsyslog(
"CAM %d: unassigned", slotNumber);
1808 tc[i]->CreateConnection();
1812 esyslog(
"ERROR: CAM %d: can't create new transport connection!", slotNumber);
1827 if (caActivationReceiver && !caActivationReceiver->IsAttached())
1830 int n = TPDU->
Tcid();
1833 tc[n]->Process(TPDU);
1838 if (!tc[i]->Process()) {
1844 if (moduleCheckTimer.TimedOut()) {
1846 if (ms != lastModuleStatus) {
1849 dbgprotocol(
"Slot %d: no module present\n", slotNumber);
1850 isyslog(
"CAM %d: no module present", slotNumber);
1851 DeleteAllConnections();
1855 dbgprotocol(
"Slot %d: module reset\n", slotNumber);
1856 isyslog(
"CAM %d: module reset", slotNumber);
1857 DeleteAllConnections();
1860 dbgprotocol(
"Slot %d: module present\n", slotNumber);
1861 isyslog(
"CAM %d: module present", slotNumber);
1864 dbgprotocol(
"Slot %d: module ready\n", slotNumber);
1865 isyslog(
"CAM %d: module ready", slotNumber);
1867 resendPmt = caProgramList.Count() > 0;
1870 esyslog(
"ERROR: unknown module status %d (%s)", ms, __FUNCTION__);
1872 lastModuleStatus = ms;
1878 processed.Broadcast();
1884 return tc[1] ? tc[1]->GetSessionByResourceId(ResourceId) : NULL;
1890 if (ciAdapter && TPDU->
Size()) {
1891 TPDU->
Dump(SlotNumber(),
true);
1892 ciAdapter->Write(TPDU->
Buffer(), TPDU->
Size());
1900 DeleteAllConnections();
1903 if (ciAdapter->Reset(slotIndex)) {
1904 resetTime = time(NULL);
1922 if (!caActivationReceiver) {
1926 d->AttachReceiver(caActivationReceiver);
1927 dsyslog(
"CAM %d: activating on device %d with channel %d (%s)", SlotNumber(), d->DeviceNumber() + 1, Channel->Number(), Channel->Name());
1936 delete caActivationReceiver;
1937 caActivationReceiver = NULL;
1942 return caActivationReceiver;
1962 return tc[1] ? tc[1]->GetCamName() : NULL;
1973 return GetSessionByResourceId(
RI_MMI);
1979 return tc[1] && tc[1]->HasUserIO();
2021 if (CaSystemIds && *CaSystemIds) {
2022 if (caProgramList.Count()) {
2023 if (caPidReceiver && caPidReceiver->HasCaPids()) {
2025 d->Detach(caPidReceiver);
2027 for (
int Loop = 1; Loop <= 2; Loop++) {
2029 if (p->modified || resendPmt) {
2031 cCiCaPmt CaPmt(CmdId, source, transponder, p->programNumber, CaSystemIds);
2034 CaPmt.
AddPid(q->pid, q->streamType);
2038 if ((Loop == 1) != Active) {
2039 if (caPidReceiver) {
2043 caPidReceiver->DelPids(CaPids);
2045 caPidReceiver->AddPids(CaPids);
2052 p->modified =
false;
2057 if (caPidReceiver && caPidReceiver->HasCaPids()) {
2059 d->AttachReceiver(caPidReceiver);
2064 cCiCaPmt CaPmt(CmdId, 0, 0, 0, NULL);
2066 if (caPidReceiver) {
2068 d->Detach(caPidReceiver);
2069 caPidReceiver->Reset();
2094 for (
const int *ids = cas->
GetCaSystemIds(); ids && *ids; ids++) {
2095 for (
const int *
id = CaSystemIds; *id;
id++) {
2109 if (p->programNumber == ProgramNumber) {
2127 if (q->pid == Pid) {
2128 if (q->active != Active) {
2139 #define STREAM_TYPE_VIDEO 0x02 2140 #define STREAM_TYPE_AUDIO 0x04 2141 #define STREAM_TYPE_PRIVATE 0x06 2148 source = Channel->
Source();
2152 for (
const int *Apid = Channel->
Apids(); *Apid; Apid++)
2154 for (
const int *Dpid = Channel->
Dpids(); *Dpid; Dpid++)
2156 for (
const int *Spid = Channel->
Spids(); *Spid; Spid++)
2163 #define QUERY_REPLY_WAIT 100 // ms to wait between checks for a reply 2169 if (!IsDecrypting())
2177 for (
const int *Apid = Channel->
Apids(); *Apid; Apid++)
2179 for (
const int *Dpid = Channel->
Dpids(); *Dpid; Dpid++)
2181 for (
const int *Spid = Channel->
Spids(); *Spid; Spid++)
2197 dsyslog(
"CAM %d: didn't reply to QUERY", SlotNumber());
2210 if (caProgramList.Count()) {
2211 caProgramList.Clear();
2219 if (caProgramList.Count()) {
2244 for (time_t t0 = time(NULL); time(NULL) - t0 < Timeout; ) {
2246 for (
cCamSlot *CamSlot = CamSlots.
First(); CamSlot; CamSlot = CamSlots.
Next(CamSlot)) {
2247 if (!CamSlot->Ready()) {
2260 #define CAM_CHECKED_TIMEOUT 15 // seconds before a CAM that has been checked for a particular channel will be checked again 2270 bool TimedOut(
void);
2272 bool CamChecked(
int CamSlotNumber);
2273 bool CamDecrypt(
int CamSlotNumber);
2274 void SetChecked(
int CamSlotNumber);
2275 void SetDecrypt(
int CamSlotNumber);
2276 void ClrChecked(
int CamSlotNumber);
2277 void ClrDecrypt(
int CamSlotNumber);
2282 channelID = ChannelID;
2283 camSlotsChecked = 0;
2284 camSlotsDecrypt = 0;
2297 camSlotsChecked = 0;
2299 return camSlotsChecked & (1 << (CamSlotNumber - 1));
2304 return camSlotsDecrypt & (1 << (CamSlotNumber - 1));
2309 camSlotsChecked |= (1 << (CamSlotNumber - 1));
2310 lastChecked = time(NULL);
2311 ClrDecrypt(CamSlotNumber);
2316 camSlotsDecrypt |= (1 << (CamSlotNumber - 1));
2317 ClrChecked(CamSlotNumber);
2322 camSlotsChecked &= ~(1 << (CamSlotNumber - 1));
2328 camSlotsDecrypt &= ~(1 << (CamSlotNumber - 1));
2333 #define CHANNEL_CAM_RELATIONS_CLEANUP_INTERVAL 3600 // seconds between cleanups 2339 lastCleanup = time(NULL);
2352 lastCleanup = time(NULL);
2361 if (ccr->ChannelID() == ChannelID)
2380 ccr->ClrChecked(CamSlotNumber);
2381 ccr->ClrDecrypt(CamSlotNumber);
2389 return ccr ? ccr->
CamChecked(CamSlotNumber) :
false;
2396 return ccr ? ccr->
CamDecrypt(CamSlotNumber) :
false;
virtual void Process(int Length=0, const uint8_t *Data=NULL)
uint8_t capmt[2048]
XXX is there a specified maximum?
cCiCaProgramData(int ProgramNumber)
#define AOT_DISPLAY_CONTROL
int DeviceNumber(void) const
Returns the number of this device (0 ... numDevices - 1).
virtual void Action(void)
Handles the attached CAM slots in a separate thread.
const int * Dpids(void) const
bool Process(cTPDU *TPDU=NULL)
cCiCaPidData(int Pid, int StreamType)
virtual bool ProvidesCa(const int *CaSystemIds)
Returns true if the CAM in this slot provides one of the given CaSystemIds.
static char * CopyString(int Length, const uint8_t *Data)
virtual ~cCiAdapter()
The derived class must call Cancel(3) in its destructor.
virtual bool HasUserIO(void)
virtual void StartActivation(void)
Puts the CAM in this slot into a mode where an inserted smart card can be activated.
cCamSlot * ItCamSlot(int &Iter)
Iterates over all added CAM slots of this adapter.
cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc)
cList< cCiCaPidData > pidList
virtual void AddPid(int ProgramNumber, int Pid, int StreamType)
Adds the given PID information to the list of PIDs.
void Add(cListObject *Object, cListObject *After=NULL)
#define AOT_PROFILE_CHANGE
bool CamChecked(int CamSlotNumber)
int Ca(int Index=0) const
virtual void StartDecrypting(void)
Triggers sending all currently active CA_PMT entries to the CAM, so that it will start decrypting...
int caSystemIds[MAXCASYSTEMIDS+1]
bool TsPayloadStart(const uchar *p)
#define MAX_CONNECTIONS_PER_CAM_SLOT
static char * GetString(int &Length, const uint8_t **Data)
#define MAX_CAM_SLOTS_PER_ADAPTER
#define AOT_APPLICATION_INFO_ENQ
static cString sprintf(const char *fmt,...) __attribute__((format(printf
#define CHANNEL_CAM_RELATIONS_CLEANUP_INTERVAL
virtual void Append(T Data)
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.
virtual void Process(int Length=0, const uint8_t *Data=NULL)
void SetCamSlot(cCamSlot *CamSlot)
Sets the given CamSlot to be used with this device.
#define dbgprotocol(a...)
virtual void Receive(uchar *Data, int Length)
This function is called from the cDevice we are attached to, and delivers one TS packet from the set ...
#define ST_CLOSE_SESSION_RESPONSE
virtual void Process(int Length=0, const uint8_t *Data=NULL)
void Detach(cFilter *Filter)
Detaches the given filter from this device.
bool AttachReceiver(cReceiver *Receiver)
Attaches the given receiver to this device.
virtual bool CanActivate(void)
Returns true if there is a CAM in this slot that can be put into activation mode. ...
static bool DumpTPDUDataTransfer
uint8_t ListManagement(void)
int GetTag(int &Length, const uint8_t **Data)
#define CPCI_NOT_SELECTED
virtual void Process(int Length=0, const uint8_t *Data=NULL)
#define AOT_APPLICATION_INFO
const int * GetCaSystemIds(void)
#define STREAM_TYPE_VIDEO
virtual ~cCaPidReceiver()
cCiSession * GetSessionByResourceId(uint32_t ResourceId)
char * GetText(int &Length, const uint8_t **Data)
int SlotIndex(void)
Returns the index of this CAM slot within its CI adapter.
int Transponder(void) const
Returns the transponder frequency in MHz, plus the polarization in case of sat.
#define ST_SESSION_NUMBER
const uint8_t * GetData(const uint8_t *Data, int &Length)
bool CamDecrypt(int CamSlotNumber)
virtual bool HasUserIO(void)
virtual eModuleStatus ModuleStatus(void)
Returns the status of the CAM in this slot.
void AddPid(int Pid, uint8_t StreamType)
virtual void AddChannel(const cChannel *Channel)
Adds all PIDs if the given Channel to the current list of PIDs.
uint32_t ResourceId(void)
void SetListManagement(uint8_t ListManagement)
#define RI_CONDITIONAL_ACCESS_SUPPORT
int TsPid(const uchar *p)
void SendCaPmt(uint8_t CmdId)
virtual bool Reset(int Slot)
Resets the CAM in the given Slot.
void CreateConnection(void)
void SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId=0, int Status=-1)
#define MODULE_RESET_TIMEOUT
static int CurrentChannel(void)
Returns the number of the current channel on the primary device.
#define ST_CLOSE_SESSION_REQUEST
cCiTransportConnection * tc
T * Next(const T *object) const
cCiSession * GetSessionByResourceId(uint32_t ResourceId)
cChannelCamRelation(tChannelID ChannelID)
uint32_t ResourceIdToInt(const uint8_t *Data)
void HandleSessions(cTPDU *TPDU)
void Process(cTPDU *TPDU=NULL)
virtual bool HasUserIO(void)
Returns true if there is a pending user interaction, which shall be retrieved via GetMenu() or GetEnq...
void SetDecrypt(tChannelID ChannelID, int CamSlotNumber)
virtual bool CanDecrypt(const cChannel *Channel)
Returns true if there is a CAM in this slot that is able to decrypt the given Channel (or at least cl...
void SendData(int Tag, int Length=0, const uint8_t *Data=NULL)
cListObject * Next(void) const
cCiSession * GetSessionBySessionId(uint16_t SessionId)
virtual cCiMenu * GetMenu(void)
Gets a pending menu, or NULL if there is no menu.
bool SendAnswer(const char *Text)
cCiConditionalAccessSupport(uint16_t SessionId, cCiTransportConnection *Tc)
bool TimedOut(void) const
#define ST_OPEN_SESSION_RESPONSE
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, int EsPid)
Gets all CA descriptors for a given channel.
#define STREAM_TYPE_AUDIO
cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds)
#define ST_OPEN_SESSION_REQUEST
virtual ~cCiTransportConnection()
cCiEnquiry * Enquiry(bool Clear=false)
#define AOT_DISPLAY_REPLY
void ClrChecked(int CamSlotNumber)
cCamSlot(cCiAdapter *CiAdapter, bool WantsTsData=false)
Creates a new CAM slot for the given CiAdapter.
bool CamDecrypt(tChannelID ChannelID, int CamSlotNumber)
virtual int Read(uint8_t *Buffer, int MaxLength)
Reads one chunk of data into the given Buffer, up to MaxLength bytes.
#define MAX_SESSIONS_PER_TC
#define STREAM_TYPE_PRIVATE
void AddCamSlot(cCamSlot *CamSlot)
Adds the given CamSlot to this CI adapter.
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
cCiTransportConnection * Tc(void)
void OpenSession(int Length, const uint8_t *Data)
const int * GetCaSystemIds(void)
void SetChecked(int CamSlotNumber)
#define CPCI_OK_DESCRAMBLING
void Dump(int SlotNumber, bool Outgoing)
bool AddPid(int Pid)
Adds the given Pid to the list of PIDs of this receiver.
static uint8_t * SetLength(uint8_t *Data, int Length)
virtual bool Reset(void)
Resets the CAM in this slot.
virtual bool IsDecrypting(void)
Returns true if the CAM in this slot is currently used for decrypting.
bool createConnectionRequested
cCiMMI(uint16_t SessionId, cCiTransportConnection *Tc)
void SendMenuAnswer(uint8_t Selection)
void SendData(int Length, const uint8_t *Data)
int Priority(void) const
Returns the priority of the current receiving session (-MAXPRIORITY..MAXPRIORITY), or IDLEPRIORITY if no receiver is currently active.
const char * GetCamName(void)
void DelPid(int Pid)
Deletes the given Pid from the list of PIDs of this receiver.
void ClrDecrypt(tChannelID ChannelID, int CamSlotNumber)
virtual void SetPid(int Pid, bool Active)
Sets the given Pid (which has previously been added through a call to AddPid()) to Active...
cCamSlot * camSlots[MAX_CAM_SLOTS_PER_ADAPTER]
const int * Apids(void) const
virtual cCiEnquiry * GetEnquiry(void)
Gets a pending enquiry, or NULL if there is no enquiry.
cChannelCamRelation * AddEntry(tChannelID ChannelID)
cCiDateTime(uint16_t SessionId, cCiTransportConnection *Tc)
#define AOT_DATE_TIME_ENQ
bool WaitForAllCamSlotsReady(int Timeout=0)
Waits until all CAM slots have become ready, or the given Timeout (seconds) has expired.
#define CAM_CHECKED_TIMEOUT
void Del(cListObject *Object, bool DeleteObject=true)
#define RI_RESOURCE_MANAGER
virtual void Process(int Length=0, const uint8_t *Data=NULL)
cChannel * GetByNumber(int Number, int SkipGap=0)
void SendPMT(cCiCaPmt *CaPmt)
void Reply(const char *s)
cCiMenu * Menu(bool Clear=false)
bool Active(void)
Checks whether the thread is still alive.
virtual eModuleStatus ModuleStatus(int Slot)
Returns the status of the CAM in the given Slot.
virtual bool HasMMI(void)
Returns 'true' if the CAM in this slot has an active MMI.
virtual ~cCaActivationReceiver()
cCaActivationReceiver(const cChannel *Channel, cCamSlot *CamSlot)
#define MODULE_CHECK_INTERVAL
tChannelID ChannelID(void)
void SetChecked(tChannelID ChannelID, int CamSlotNumber)
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...
cCiEnquiry * fetchedEnquiry
void ClrDecrypt(int CamSlotNumber)
bool TsIsScrambled(const uchar *p)
cChannelCamRelations ChannelCamRelations
virtual bool Ready(void)
Returns 'true' if the CAM in this slot is ready to decrypt.
void DeleteConnection(void)
virtual void CancelActivation(void)
Cancels a previously started activation (if any).
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...
int Priority(void)
Returns the priority of the device this slot is currently assigned to, or IDLEPRIORITY if it is not a...
bool CamChecked(tChannelID ChannelID, int CamSlotNumber)
virtual void StopDecrypting(void)
Clears the list of CA_PMT entries and tells the CAM to stop decrypting.
void ClrChecked(tChannelID ChannelID, int CamSlotNumber)
void AddCaDescriptors(int Length, const uint8_t *Data)
const uint8_t * GetData(const uint8_t *Data, int &Length)
void Reset(int CamSlotNumber)
const int * Spids(void) const
cCiTransportConnection(cCamSlot *CamSlot, uint8_t Tcid)
virtual bool EnterMenu(void)
Requests the CAM in this slot to start its menu.
int GetCaPids(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, int *Pids)
Gets all CA pids for a given channel.
#define ST_CREATE_SESSION_RESPONSE
#define QUERY_REPLY_TIMEOUT
static const uint8_t * GetLength(const uint8_t *Data, int &Length)
bool deleteConnectionRequested
void CloseSession(uint16_t SessionId)
cChannelCamRelation * GetEntry(tChannelID ChannelID)
#define RI_APPLICATION_INFORMATION
bool Assign(cDevice *Device, bool Query=false)
Assigns this CAM slot to the given Device, if this is possible.
virtual void Activate(bool On)
This function is called just before the cReceiver gets attached to (On == true) and right after it ge...
virtual bool IsActivating(void)
Returns true if this CAM slot is currently activating a smart card.
void SendTPDU(uint8_t Tag, int Length=0, const uint8_t *Data=NULL)
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
cChannelCamRelations(void)
void DeleteAllConnections(void)
bool RepliesToQuery(void)
const uint8_t * Data(int &Length)
int SlotNumber(void)
Returns the number of this CAM slot within the whole system.
The cDevice class is the base from which actual devices can be derived.
static bool DebugProtocol
virtual void Receive(uchar *Data, int Length)
This function is called from the cDevice we are attached to, and delivers one TS packet from the set ...
cCiResourceManager(uint16_t SessionId, cCiTransportConnection *Tc)
void SetDecrypt(int CamSlotNumber)