vdr
1.7.27
|
00001 /* 00002 * ci.c: Common Interface 00003 * 00004 * See the main source file 'vdr.c' for copyright information and 00005 * how to reach the author. 00006 * 00007 * $Id: ci.c 2.8 2012/02/29 10:24:41 kls Exp $ 00008 */ 00009 00010 #include "ci.h" 00011 #include <ctype.h> 00012 #include <linux/dvb/ca.h> 00013 #include <malloc.h> 00014 #include <netinet/in.h> 00015 #include <poll.h> 00016 #include <string.h> 00017 #include <sys/ioctl.h> 00018 #include <time.h> 00019 #include <unistd.h> 00020 #include "device.h" 00021 #include "pat.h" 00022 #include "tools.h" 00023 00024 // Set these to 'true' for debug output: 00025 static bool DumpTPDUDataTransfer = false; 00026 static bool DebugProtocol = false; 00027 static bool DumpPolls = false; 00028 static bool DumpDateTime = false; 00029 00030 #define dbgprotocol(a...) do { if (DebugProtocol) fprintf(stderr, a); } while (0) 00031 00032 // --- Helper functions ------------------------------------------------------ 00033 00034 #define SIZE_INDICATOR 0x80 00035 00036 static const uint8_t *GetLength(const uint8_t *Data, int &Length) 00040 { 00041 Length = *Data++; 00042 if ((Length & SIZE_INDICATOR) != 0) { 00043 int l = Length & ~SIZE_INDICATOR; 00044 Length = 0; 00045 for (int i = 0; i < l; i++) 00046 Length = (Length << 8) | *Data++; 00047 } 00048 return Data; 00049 } 00050 00051 static uint8_t *SetLength(uint8_t *Data, int Length) 00054 { 00055 uint8_t *p = Data; 00056 if (Length < 128) 00057 *p++ = Length; 00058 else { 00059 int n = sizeof(Length); 00060 for (int i = n - 1; i >= 0; i--) { 00061 int b = (Length >> (8 * i)) & 0xFF; 00062 if (p != Data || b) 00063 *++p = b; 00064 } 00065 *Data = (p - Data) | SIZE_INDICATOR; 00066 p++; 00067 } 00068 return p; 00069 } 00070 00071 static char *CopyString(int Length, const uint8_t *Data) 00074 { 00075 // Some CAMs send funny characters at the beginning of strings. 00076 // Let's just skip them: 00077 while (Length > 0 && (*Data == ' ' || *Data == 0x05 || *Data == 0x96 || *Data == 0x97)) { 00078 Length--; 00079 Data++; 00080 } 00081 char *s = MALLOC(char, Length + 1); 00082 strncpy(s, (char *)Data, Length); 00083 s[Length] = 0; 00084 // The character 0x8A is used as newline, so let's put a real '\n' in there: 00085 strreplace(s, 0x8A, '\n'); 00086 return s; 00087 } 00088 00089 static char *GetString(int &Length, const uint8_t **Data) 00093 { 00094 if (Length > 0 && Data && *Data) { 00095 int l = 0; 00096 const uint8_t *d = GetLength(*Data, l); 00097 char *s = CopyString(l, d); 00098 Length -= d - *Data + l; 00099 *Data = d + l; 00100 return s; 00101 } 00102 return NULL; 00103 } 00104 00105 // --- cTPDU ----------------------------------------------------------------- 00106 00107 #define MAX_TPDU_SIZE 2048 00108 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4) 00109 00110 #define DATA_INDICATOR 0x80 00111 00112 #define T_SB 0x80 00113 #define T_RCV 0x81 00114 #define T_CREATE_TC 0x82 00115 #define T_CTC_REPLY 0x83 00116 #define T_DELETE_TC 0x84 00117 #define T_DTC_REPLY 0x85 00118 #define T_REQUEST_TC 0x86 00119 #define T_NEW_TC 0x87 00120 #define T_TC_ERROR 0x88 00121 #define T_DATA_LAST 0xA0 00122 #define T_DATA_MORE 0xA1 00123 00124 class cTPDU { 00125 private: 00126 int size; 00127 uint8_t buffer[MAX_TPDU_SIZE]; 00128 const uint8_t *GetData(const uint8_t *Data, int &Length); 00129 public: 00130 cTPDU(void) { size = 0; } 00131 cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length = 0, const uint8_t *Data = NULL); 00132 uint8_t Slot(void) { return buffer[0]; } 00133 uint8_t Tcid(void) { return buffer[1]; } 00134 uint8_t Tag(void) { return buffer[2]; } 00135 const uint8_t *Data(int &Length) { return GetData(buffer + 3, Length); } 00136 uint8_t Status(void); 00137 uint8_t *Buffer(void) { return buffer; } 00138 int Size(void) { return size; } 00139 void SetSize(int Size) { size = Size; } 00140 int MaxSize(void) { return sizeof(buffer); } 00141 void Dump(int SlotNumber, bool Outgoing); 00142 }; 00143 00144 cTPDU::cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length, const uint8_t *Data) 00145 { 00146 size = 0; 00147 buffer[0] = Slot; 00148 buffer[1] = Tcid; 00149 buffer[2] = Tag; 00150 switch (Tag) { 00151 case T_RCV: 00152 case T_CREATE_TC: 00153 case T_CTC_REPLY: 00154 case T_DELETE_TC: 00155 case T_DTC_REPLY: 00156 case T_REQUEST_TC: 00157 buffer[3] = 1; // length 00158 buffer[4] = Tcid; 00159 size = 5; 00160 break; 00161 case T_NEW_TC: 00162 case T_TC_ERROR: 00163 if (Length == 1) { 00164 buffer[3] = 2; // length 00165 buffer[4] = Tcid; 00166 buffer[5] = Data[0]; 00167 size = 6; 00168 } 00169 else 00170 esyslog("ERROR: invalid data length for TPDU tag 0x%02X: %d (%d/%d)", Tag, Length, Slot, Tcid); 00171 break; 00172 case T_DATA_LAST: 00173 case T_DATA_MORE: 00174 if (Length <= MAX_TPDU_DATA) { 00175 uint8_t *p = buffer + 3; 00176 p = SetLength(p, Length + 1); 00177 *p++ = Tcid; 00178 if (Length) 00179 memcpy(p, Data, Length); 00180 size = Length + (p - buffer); 00181 } 00182 else 00183 esyslog("ERROR: invalid data length for TPDU tag 0x%02X: %d (%d/%d)", Tag, Length, Slot, Tcid); 00184 break; 00185 default: 00186 esyslog("ERROR: unknown TPDU tag: 0x%02X (%d/%d)", Tag, Slot, Tcid); 00187 } 00188 } 00189 00190 void cTPDU::Dump(int SlotNumber, bool Outgoing) 00191 { 00192 if (DumpTPDUDataTransfer && (DumpPolls || Tag() != T_SB)) { 00193 #define MAX_DUMP 256 00194 fprintf(stderr, " %d: %s ", SlotNumber, Outgoing ? "-->" : "<--"); 00195 for (int i = 0; i < size && i < MAX_DUMP; i++) 00196 fprintf(stderr, "%02X ", buffer[i]); 00197 fprintf(stderr, "%s\n", size >= MAX_DUMP ? "..." : ""); 00198 if (!Outgoing) { 00199 fprintf(stderr, " "); 00200 for (int i = 0; i < size && i < MAX_DUMP; i++) 00201 fprintf(stderr, "%2c ", isprint(buffer[i]) ? buffer[i] : '.'); 00202 fprintf(stderr, "%s\n", size >= MAX_DUMP ? "..." : ""); 00203 } 00204 } 00205 } 00206 00207 const uint8_t *cTPDU::GetData(const uint8_t *Data, int &Length) 00208 { 00209 if (size) { 00210 Data = GetLength(Data, Length); 00211 if (Length) { 00212 Length--; // the first byte is always the tcid 00213 return Data + 1; 00214 } 00215 } 00216 return NULL; 00217 } 00218 00219 uint8_t cTPDU::Status(void) 00220 { 00221 if (size >= 4 && buffer[size - 4] == T_SB && buffer[size - 3] == 2) 00222 return buffer[size - 1]; 00223 return 0; 00224 } 00225 00226 // --- cCiTransportConnection ------------------------------------------------ 00227 00228 #define MAX_SESSIONS_PER_TC 16 00229 00230 class cCiTransportConnection { 00231 private: 00232 enum eState { stIDLE, stCREATION, stACTIVE, stDELETION }; 00233 cCamSlot *camSlot; 00234 uint8_t tcid; 00235 eState state; 00236 bool createConnectionRequested; 00237 bool deleteConnectionRequested; 00238 bool hasUserIO; 00239 cTimeMs alive; 00240 cTimeMs timer; 00241 cCiSession *sessions[MAX_SESSIONS_PER_TC + 1]; // session numbering starts with 1 00242 void SendTPDU(uint8_t Tag, int Length = 0, const uint8_t *Data = NULL); 00243 void SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId = 0, int Status = -1); 00244 void Poll(void); 00245 uint32_t ResourceIdToInt(const uint8_t *Data); 00246 cCiSession *GetSessionBySessionId(uint16_t SessionId); 00247 void OpenSession(int Length, const uint8_t *Data); 00248 void CloseSession(uint16_t SessionId); 00249 void HandleSessions(cTPDU *TPDU); 00250 public: 00251 cCiTransportConnection(cCamSlot *CamSlot, uint8_t Tcid); 00252 virtual ~cCiTransportConnection(); 00253 cCamSlot *CamSlot(void) { return camSlot; } 00254 uint8_t Tcid(void) const { return tcid; } 00255 void CreateConnection(void) { createConnectionRequested = true; } 00256 void DeleteConnection(void) { deleteConnectionRequested = true; } 00257 const char *GetCamName(void); 00258 bool Ready(void); 00259 bool HasUserIO(void) { return hasUserIO; } 00260 void SendData(int Length, const uint8_t *Data); 00261 bool Process(cTPDU *TPDU = NULL); 00262 cCiSession *GetSessionByResourceId(uint32_t ResourceId); 00263 }; 00264 00265 // --- cCiSession ------------------------------------------------------------ 00266 00267 // Session Tags: 00268 00269 #define ST_SESSION_NUMBER 0x90 00270 #define ST_OPEN_SESSION_REQUEST 0x91 00271 #define ST_OPEN_SESSION_RESPONSE 0x92 00272 #define ST_CREATE_SESSION 0x93 00273 #define ST_CREATE_SESSION_RESPONSE 0x94 00274 #define ST_CLOSE_SESSION_REQUEST 0x95 00275 #define ST_CLOSE_SESSION_RESPONSE 0x96 00276 00277 // Session Status: 00278 00279 #define SS_OK 0x00 00280 #define SS_NOT_ALLOCATED 0xF0 00281 00282 // Resource Identifiers: 00283 00284 #define RI_RESOURCE_MANAGER 0x00010041 00285 #define RI_APPLICATION_INFORMATION 0x00020041 00286 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041 00287 #define RI_HOST_CONTROL 0x00200041 00288 #define RI_DATE_TIME 0x00240041 00289 #define RI_MMI 0x00400041 00290 00291 // Application Object Tags: 00292 00293 #define AOT_NONE 0x000000 00294 #define AOT_PROFILE_ENQ 0x9F8010 00295 #define AOT_PROFILE 0x9F8011 00296 #define AOT_PROFILE_CHANGE 0x9F8012 00297 #define AOT_APPLICATION_INFO_ENQ 0x9F8020 00298 #define AOT_APPLICATION_INFO 0x9F8021 00299 #define AOT_ENTER_MENU 0x9F8022 00300 #define AOT_CA_INFO_ENQ 0x9F8030 00301 #define AOT_CA_INFO 0x9F8031 00302 #define AOT_CA_PMT 0x9F8032 00303 #define AOT_CA_PMT_REPLY 0x9F8033 00304 #define AOT_TUNE 0x9F8400 00305 #define AOT_REPLACE 0x9F8401 00306 #define AOT_CLEAR_REPLACE 0x9F8402 00307 #define AOT_ASK_RELEASE 0x9F8403 00308 #define AOT_DATE_TIME_ENQ 0x9F8440 00309 #define AOT_DATE_TIME 0x9F8441 00310 #define AOT_CLOSE_MMI 0x9F8800 00311 #define AOT_DISPLAY_CONTROL 0x9F8801 00312 #define AOT_DISPLAY_REPLY 0x9F8802 00313 #define AOT_TEXT_LAST 0x9F8803 00314 #define AOT_TEXT_MORE 0x9F8804 00315 #define AOT_KEYPAD_CONTROL 0x9F8805 00316 #define AOT_KEYPRESS 0x9F8806 00317 #define AOT_ENQ 0x9F8807 00318 #define AOT_ANSW 0x9F8808 00319 #define AOT_MENU_LAST 0x9F8809 00320 #define AOT_MENU_MORE 0x9F880A 00321 #define AOT_MENU_ANSW 0x9F880B 00322 #define AOT_LIST_LAST 0x9F880C 00323 #define AOT_LIST_MORE 0x9F880D 00324 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E 00325 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F 00326 #define AOT_DISPLAY_MESSAGE 0x9F8810 00327 #define AOT_SCENE_END_MARK 0x9F8811 00328 #define AOT_SCENE_DONE 0x9F8812 00329 #define AOT_SCENE_CONTROL 0x9F8813 00330 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814 00331 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815 00332 #define AOT_FLUSH_DOWNLOAD 0x9F8816 00333 #define AOT_DOWNLOAD_REPLY 0x9F8817 00334 #define AOT_COMMS_CMD 0x9F8C00 00335 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01 00336 #define AOT_COMMS_REPLY 0x9F8C02 00337 #define AOT_COMMS_SEND_LAST 0x9F8C03 00338 #define AOT_COMMS_SEND_MORE 0x9F8C04 00339 #define AOT_COMMS_RCV_LAST 0x9F8C05 00340 #define AOT_COMMS_RCV_MORE 0x9F8C06 00341 00342 class cCiSession { 00343 private: 00344 uint16_t sessionId; 00345 uint32_t resourceId; 00346 cCiTransportConnection *tc; 00347 protected: 00348 int GetTag(int &Length, const uint8_t **Data); 00349 const uint8_t *GetData(const uint8_t *Data, int &Length); 00350 void SendData(int Tag, int Length = 0, const uint8_t *Data = NULL); 00351 cCiTransportConnection *Tc(void) { return tc; } 00352 public: 00353 cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc); 00354 virtual ~cCiSession(); 00355 uint16_t SessionId(void) { return sessionId; } 00356 uint32_t ResourceId(void) { return resourceId; } 00357 virtual bool HasUserIO(void) { return false; } 00358 virtual void Process(int Length = 0, const uint8_t *Data = NULL); 00359 }; 00360 00361 cCiSession::cCiSession(uint16_t SessionId, uint32_t ResourceId, cCiTransportConnection *Tc) 00362 { 00363 sessionId = SessionId; 00364 resourceId = ResourceId; 00365 tc = Tc; 00366 } 00367 00368 cCiSession::~cCiSession() 00369 { 00370 } 00371 00372 int cCiSession::GetTag(int &Length, const uint8_t **Data) 00376 { 00377 if (Length >= 3 && Data && *Data) { 00378 int t = 0; 00379 for (int i = 0; i < 3; i++) 00380 t = (t << 8) | *(*Data)++; 00381 Length -= 3; 00382 return t; 00383 } 00384 return AOT_NONE; 00385 } 00386 00387 const uint8_t *cCiSession::GetData(const uint8_t *Data, int &Length) 00388 { 00389 Data = GetLength(Data, Length); 00390 return Length ? Data : NULL; 00391 } 00392 00393 void cCiSession::SendData(int Tag, int Length, const uint8_t *Data) 00394 { 00395 uint8_t buffer[2048]; 00396 uint8_t *p = buffer; 00397 *p++ = ST_SESSION_NUMBER; 00398 *p++ = 0x02; 00399 *p++ = (sessionId >> 8) & 0xFF; 00400 *p++ = sessionId & 0xFF; 00401 *p++ = (Tag >> 16) & 0xFF; 00402 *p++ = (Tag >> 8) & 0xFF; 00403 *p++ = Tag & 0xFF; 00404 p = SetLength(p, Length); 00405 if (p - buffer + Length < int(sizeof(buffer))) { 00406 memcpy(p, Data, Length); 00407 p += Length; 00408 tc->SendData(p - buffer, buffer); 00409 } 00410 else 00411 esyslog("ERROR: CAM %d: data length (%d) exceeds buffer size", Tc()->CamSlot()->SlotNumber(), Length); 00412 } 00413 00414 void cCiSession::Process(int Length, const uint8_t *Data) 00415 { 00416 } 00417 00418 // --- cCiResourceManager ---------------------------------------------------- 00419 00420 class cCiResourceManager : public cCiSession { 00421 private: 00422 int state; 00423 public: 00424 cCiResourceManager(uint16_t SessionId, cCiTransportConnection *Tc); 00425 virtual void Process(int Length = 0, const uint8_t *Data = NULL); 00426 }; 00427 00428 cCiResourceManager::cCiResourceManager(uint16_t SessionId, cCiTransportConnection *Tc) 00429 :cCiSession(SessionId, RI_RESOURCE_MANAGER, Tc) 00430 { 00431 dbgprotocol("Slot %d: new Resource Manager (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId); 00432 state = 0; 00433 } 00434 00435 void cCiResourceManager::Process(int Length, const uint8_t *Data) 00436 { 00437 if (Data) { 00438 int Tag = GetTag(Length, &Data); 00439 switch (Tag) { 00440 case AOT_PROFILE_ENQ: { 00441 dbgprotocol("Slot %d: <== Profile Enquiry (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 00442 uint32_t resources[] = { htonl(RI_RESOURCE_MANAGER), 00443 htonl(RI_APPLICATION_INFORMATION), 00444 htonl(RI_CONDITIONAL_ACCESS_SUPPORT), 00445 htonl(RI_DATE_TIME), 00446 htonl(RI_MMI) 00447 }; 00448 dbgprotocol("Slot %d: ==> Profile (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 00449 SendData(AOT_PROFILE, sizeof(resources), (uint8_t*)resources); 00450 state = 3; 00451 } 00452 break; 00453 case AOT_PROFILE: { 00454 dbgprotocol("Slot %d: <== Profile (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 00455 if (state == 1) { 00456 int l = 0; 00457 const uint8_t *d = GetData(Data, l); 00458 if (l > 0 && d) 00459 esyslog("ERROR: CAM %d: resource manager: unexpected data", Tc()->CamSlot()->SlotNumber()); 00460 dbgprotocol("Slot %d: ==> Profile Change (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 00461 SendData(AOT_PROFILE_CHANGE); 00462 state = 2; 00463 } 00464 else { 00465 esyslog("ERROR: CAM %d: resource manager: unexpected tag %06X in state %d", Tc()->CamSlot()->SlotNumber(), Tag, state); 00466 } 00467 } 00468 break; 00469 default: esyslog("ERROR: CAM %d: resource manager: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag); 00470 } 00471 } 00472 else if (state == 0) { 00473 dbgprotocol("Slot %d: ==> Profile Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 00474 SendData(AOT_PROFILE_ENQ); 00475 state = 1; 00476 } 00477 } 00478 00479 // --- cCiApplicationInformation --------------------------------------------- 00480 00481 class cCiApplicationInformation : public cCiSession { 00482 private: 00483 int state; 00484 uint8_t applicationType; 00485 uint16_t applicationManufacturer; 00486 uint16_t manufacturerCode; 00487 char *menuString; 00488 public: 00489 cCiApplicationInformation(uint16_t SessionId, cCiTransportConnection *Tc); 00490 virtual ~cCiApplicationInformation(); 00491 virtual void Process(int Length = 0, const uint8_t *Data = NULL); 00492 bool EnterMenu(void); 00493 const char *GetMenuString(void) { return menuString; } 00494 }; 00495 00496 cCiApplicationInformation::cCiApplicationInformation(uint16_t SessionId, cCiTransportConnection *Tc) 00497 :cCiSession(SessionId, RI_APPLICATION_INFORMATION, Tc) 00498 { 00499 dbgprotocol("Slot %d: new Application Information (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId); 00500 state = 0; 00501 menuString = NULL; 00502 } 00503 00504 cCiApplicationInformation::~cCiApplicationInformation() 00505 { 00506 free(menuString); 00507 } 00508 00509 void cCiApplicationInformation::Process(int Length, const uint8_t *Data) 00510 { 00511 if (Data) { 00512 int Tag = GetTag(Length, &Data); 00513 switch (Tag) { 00514 case AOT_APPLICATION_INFO: { 00515 dbgprotocol("Slot %d: <== Application Info (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 00516 int l = 0; 00517 const uint8_t *d = GetData(Data, l); 00518 if ((l -= 1) < 0) break; 00519 applicationType = *d++; 00520 if ((l -= 2) < 0) break; 00521 applicationManufacturer = ntohs(get_unaligned((uint16_t *)d)); 00522 d += 2; 00523 if ((l -= 2) < 0) break; 00524 manufacturerCode = ntohs(get_unaligned((uint16_t *)d)); 00525 d += 2; 00526 free(menuString); 00527 menuString = GetString(l, &d); 00528 isyslog("CAM %d: %s, %02X, %04X, %04X", Tc()->CamSlot()->SlotNumber(), menuString, applicationType, applicationManufacturer, manufacturerCode); 00529 state = 2; 00530 } 00531 break; 00532 default: esyslog("ERROR: CAM %d: application information: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag); 00533 } 00534 } 00535 else if (state == 0) { 00536 dbgprotocol("Slot %d: ==> Application Info Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 00537 SendData(AOT_APPLICATION_INFO_ENQ); 00538 state = 1; 00539 } 00540 } 00541 00542 bool cCiApplicationInformation::EnterMenu(void) 00543 { 00544 if (state == 2) { 00545 dbgprotocol("Slot %d: ==> Enter Menu (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 00546 SendData(AOT_ENTER_MENU); 00547 return true; 00548 } 00549 return false; 00550 } 00551 00552 // --- cCiCaPmt -------------------------------------------------------------- 00553 00554 #define MAXCASYSTEMIDS 64 00555 00556 // Ca Pmt List Management: 00557 00558 #define CPLM_MORE 0x00 00559 #define CPLM_FIRST 0x01 00560 #define CPLM_LAST 0x02 00561 #define CPLM_ONLY 0x03 00562 #define CPLM_ADD 0x04 00563 #define CPLM_UPDATE 0x05 00564 00565 // Ca Pmt Cmd Ids: 00566 00567 #define CPCI_OK_DESCRAMBLING 0x01 00568 #define CPCI_OK_MMI 0x02 00569 #define CPCI_QUERY 0x03 00570 #define CPCI_NOT_SELECTED 0x04 00571 00572 class cCiCaPmt : public cListObject { 00573 friend class cCiConditionalAccessSupport; 00574 private: 00575 uint8_t cmdId; 00576 int length; 00577 int esInfoLengthPos; 00578 uint8_t capmt[2048]; 00579 int source; 00580 int transponder; 00581 int programNumber; 00582 int caSystemIds[MAXCASYSTEMIDS + 1]; // list is zero terminated! 00583 void AddCaDescriptors(int Length, const uint8_t *Data); 00584 public: 00585 cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds); 00586 uint8_t CmdId(void) { return cmdId; } 00587 void SetListManagement(uint8_t ListManagement); 00588 uint8_t ListManagement(void) { return capmt[0]; } 00589 void AddPid(int Pid, uint8_t StreamType); 00590 }; 00591 00592 cCiCaPmt::cCiCaPmt(uint8_t CmdId, int Source, int Transponder, int ProgramNumber, const int *CaSystemIds) 00593 { 00594 cmdId = CmdId; 00595 source = Source; 00596 transponder = Transponder; 00597 programNumber = ProgramNumber; 00598 int i = 0; 00599 if (CaSystemIds) { 00600 for (; CaSystemIds[i]; i++) 00601 caSystemIds[i] = CaSystemIds[i]; 00602 } 00603 caSystemIds[i] = 0; 00604 uint8_t caDescriptors[512]; 00605 int caDescriptorsLength = GetCaDescriptors(source, transponder, programNumber, caSystemIds, sizeof(caDescriptors), caDescriptors, 0); 00606 length = 0; 00607 capmt[length++] = CPLM_ONLY; 00608 capmt[length++] = (ProgramNumber >> 8) & 0xFF; 00609 capmt[length++] = ProgramNumber & 0xFF; 00610 capmt[length++] = 0x01; // version_number, current_next_indicator - apparently vn doesn't matter, but cni must be 1 00611 esInfoLengthPos = length; 00612 capmt[length++] = 0x00; // program_info_length H (at program level) 00613 capmt[length++] = 0x00; // program_info_length L 00614 AddCaDescriptors(caDescriptorsLength, caDescriptors); 00615 } 00616 00617 void cCiCaPmt::SetListManagement(uint8_t ListManagement) 00618 { 00619 capmt[0] = ListManagement; 00620 } 00621 00622 void cCiCaPmt::AddPid(int Pid, uint8_t StreamType) 00623 { 00624 if (Pid) { 00625 uint8_t caDescriptors[512]; 00626 int caDescriptorsLength = GetCaDescriptors(source, transponder, programNumber, caSystemIds, sizeof(caDescriptors), caDescriptors, Pid); 00627 //XXX buffer overflow check??? 00628 capmt[length++] = StreamType; 00629 capmt[length++] = (Pid >> 8) & 0xFF; 00630 capmt[length++] = Pid & 0xFF; 00631 esInfoLengthPos = length; 00632 capmt[length++] = 0x00; // ES_info_length H (at ES level) 00633 capmt[length++] = 0x00; // ES_info_length L 00634 AddCaDescriptors(caDescriptorsLength, caDescriptors); 00635 } 00636 } 00637 00638 void cCiCaPmt::AddCaDescriptors(int Length, const uint8_t *Data) 00639 { 00640 if (esInfoLengthPos) { 00641 if (length + Length < int(sizeof(capmt))) { 00642 if (Length || cmdId == CPCI_QUERY) { 00643 capmt[length++] = cmdId; 00644 memcpy(capmt + length, Data, Length); 00645 length += Length; 00646 int l = length - esInfoLengthPos - 2; 00647 capmt[esInfoLengthPos] = (l >> 8) & 0xFF; 00648 capmt[esInfoLengthPos + 1] = l & 0xFF; 00649 } 00650 } 00651 else 00652 esyslog("ERROR: buffer overflow in CA descriptor"); 00653 esInfoLengthPos = 0; 00654 } 00655 else 00656 esyslog("ERROR: adding CA descriptor without Pid!"); 00657 } 00658 00659 // --- cCiConditionalAccessSupport ------------------------------------------- 00660 00661 // CA Enable Ids: 00662 00663 #define CAEI_POSSIBLE 0x01 00664 #define CAEI_POSSIBLE_COND_PURCHASE 0x02 00665 #define CAEI_POSSIBLE_COND_TECHNICAL 0x03 00666 #define CAEI_NOT_POSSIBLE_ENTITLEMENT 0x71 00667 #define CAEI_NOT_POSSIBLE_TECHNICAL 0x73 00668 00669 #define CA_ENABLE_FLAG 0x80 00670 00671 #define CA_ENABLE(x) (((x) & CA_ENABLE_FLAG) ? (x) & ~CA_ENABLE_FLAG : 0) 00672 00673 #define QUERY_WAIT_TIME 1000 // ms to wait before sending a query 00674 #define QUERY_REPLY_TIMEOUT 2000 // ms to wait for a reply to a query 00675 00676 class cCiConditionalAccessSupport : public cCiSession { 00677 private: 00678 int state; 00679 int numCaSystemIds; 00680 int caSystemIds[MAXCASYSTEMIDS + 1]; // list is zero terminated! 00681 bool repliesToQuery; 00682 cTimeMs timer; 00683 public: 00684 cCiConditionalAccessSupport(uint16_t SessionId, cCiTransportConnection *Tc); 00685 virtual void Process(int Length = 0, const uint8_t *Data = NULL); 00686 const int *GetCaSystemIds(void) { return caSystemIds; } 00687 void SendPMT(cCiCaPmt *CaPmt); 00688 bool RepliesToQuery(void) { return repliesToQuery; } 00689 bool Ready(void) { return state >= 4; } 00690 bool ReceivedReply(void) { return state >= 5; } 00691 bool CanDecrypt(void) { return state == 6; } 00692 }; 00693 00694 cCiConditionalAccessSupport::cCiConditionalAccessSupport(uint16_t SessionId, cCiTransportConnection *Tc) 00695 :cCiSession(SessionId, RI_CONDITIONAL_ACCESS_SUPPORT, Tc) 00696 { 00697 dbgprotocol("Slot %d: new Conditional Access Support (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId); 00698 state = 0; // inactive 00699 caSystemIds[numCaSystemIds = 0] = 0; 00700 repliesToQuery = false; 00701 } 00702 00703 void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data) 00704 { 00705 if (Data) { 00706 int Tag = GetTag(Length, &Data); 00707 switch (Tag) { 00708 case AOT_CA_INFO: { 00709 dbgprotocol("Slot %d: <== Ca Info (%d)", Tc()->CamSlot()->SlotNumber(), SessionId()); 00710 numCaSystemIds = 0; 00711 int l = 0; 00712 const uint8_t *d = GetData(Data, l); 00713 while (l > 1) { 00714 uint16_t id = ((uint16_t)(*d) << 8) | *(d + 1); 00715 dbgprotocol(" %04X", id); 00716 d += 2; 00717 l -= 2; 00718 if (numCaSystemIds < MAXCASYSTEMIDS) 00719 caSystemIds[numCaSystemIds++] = id; 00720 else { 00721 esyslog("ERROR: CAM %d: too many CA system IDs!", Tc()->CamSlot()->SlotNumber()); 00722 break; 00723 } 00724 } 00725 caSystemIds[numCaSystemIds] = 0; 00726 dbgprotocol("\n"); 00727 if (state == 1) { 00728 timer.Set(QUERY_WAIT_TIME); // WORKAROUND: Alphacrypt 3.09 doesn't reply to QUERY immediately after reset 00729 state = 2; // got ca info 00730 } 00731 } 00732 break; 00733 case AOT_CA_PMT_REPLY: { 00734 dbgprotocol("Slot %d: <== Ca Pmt Reply (%d)", Tc()->CamSlot()->SlotNumber(), SessionId()); 00735 if (!repliesToQuery) { 00736 dsyslog("CAM %d: replies to QUERY - multi channel decryption possible", Tc()->CamSlot()->SlotNumber()); 00737 repliesToQuery = true; 00738 } 00739 state = 5; // got ca pmt reply 00740 int l = 0; 00741 const uint8_t *d = GetData(Data, l); 00742 if (l > 1) { 00743 uint16_t pnr = ((uint16_t)(*d) << 8) | *(d + 1); 00744 dbgprotocol(" %d", pnr); 00745 d += 2; 00746 l -= 2; 00747 if (l > 0) { 00748 dbgprotocol(" %02X", *d); 00749 d += 1; 00750 l -= 1; 00751 if (l > 0) { 00752 if (l % 3 == 0 && l > 1) { 00753 // The EN50221 standard defines that the next byte is supposed 00754 // to be the CA_enable value at programme level. However, there are 00755 // CAMs (for instance the AlphaCrypt with firmware <= 3.05) that 00756 // insert a two byte length field here. 00757 // This is a workaround to skip this length field: 00758 uint16_t len = ((uint16_t)(*d) << 8) | *(d + 1); 00759 if (len == l - 2) { 00760 d += 2; 00761 l -= 2; 00762 } 00763 } 00764 unsigned char caepl = *d; 00765 dbgprotocol(" %02X", caepl); 00766 d += 1; 00767 l -= 1; 00768 bool ok = true; 00769 if (l <= 2) 00770 ok = CA_ENABLE(caepl) == CAEI_POSSIBLE; 00771 while (l > 2) { 00772 uint16_t pid = ((uint16_t)(*d) << 8) | *(d + 1); 00773 unsigned char caees = *(d + 2); 00774 dbgprotocol(" %d=%02X", pid, caees); 00775 d += 3; 00776 l -= 3; 00777 if (CA_ENABLE(caees) != CAEI_POSSIBLE) 00778 ok = false; 00779 } 00780 if (ok) 00781 state = 6; // descrambling possible 00782 } 00783 } 00784 } 00785 dbgprotocol("\n"); 00786 } 00787 break; 00788 default: esyslog("ERROR: CAM %d: conditional access support: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag); 00789 } 00790 } 00791 else if (state == 0) { 00792 dbgprotocol("Slot %d: ==> Ca Info Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 00793 SendData(AOT_CA_INFO_ENQ); 00794 state = 1; // enquired ca info 00795 } 00796 else if (state == 2 && timer.TimedOut()) { 00797 cCiCaPmt CaPmt(CPCI_QUERY, 0, 0, 0, NULL); 00798 SendPMT(&CaPmt); 00799 timer.Set(QUERY_REPLY_TIMEOUT); 00800 state = 3; // waiting for reply 00801 } 00802 else if (state == 3 && timer.TimedOut()) { 00803 dsyslog("CAM %d: doesn't reply to QUERY - only a single channel can be decrypted", Tc()->CamSlot()->SlotNumber()); 00804 state = 4; // normal operation 00805 } 00806 } 00807 00808 void cCiConditionalAccessSupport::SendPMT(cCiCaPmt *CaPmt) 00809 { 00810 if (CaPmt && state >= 2) { 00811 dbgprotocol("Slot %d: ==> Ca Pmt (%d) %d %d\n", Tc()->CamSlot()->SlotNumber(), SessionId(), CaPmt->ListManagement(), CaPmt->CmdId()); 00812 SendData(AOT_CA_PMT, CaPmt->length, CaPmt->capmt); 00813 state = 4; // sent ca pmt 00814 } 00815 } 00816 00817 // --- cCiDateTime ----------------------------------------------------------- 00818 00819 class cCiDateTime : public cCiSession { 00820 private: 00821 int interval; 00822 time_t lastTime; 00823 void SendDateTime(void); 00824 public: 00825 cCiDateTime(uint16_t SessionId, cCiTransportConnection *Tc); 00826 virtual void Process(int Length = 0, const uint8_t *Data = NULL); 00827 }; 00828 00829 cCiDateTime::cCiDateTime(uint16_t SessionId, cCiTransportConnection *Tc) 00830 :cCiSession(SessionId, RI_DATE_TIME, Tc) 00831 { 00832 interval = 0; 00833 lastTime = 0; 00834 dbgprotocol("Slot %d: new Date Time (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId); 00835 } 00836 00837 void cCiDateTime::SendDateTime(void) 00838 { 00839 time_t t = time(NULL); 00840 struct tm tm_gmt; 00841 struct tm tm_loc; 00842 if (gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc)) { 00843 int Y = tm_gmt.tm_year; 00844 int M = tm_gmt.tm_mon + 1; 00845 int D = tm_gmt.tm_mday; 00846 int L = (M == 1 || M == 2) ? 1 : 0; 00847 int MJD = 14956 + D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001); 00848 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10)) 00849 struct tTime { uint16_t mjd; uint8_t h, m, s; short offset; }; 00850 tTime T = { mjd : htons(MJD), h : DEC2BCD(tm_gmt.tm_hour), m : DEC2BCD(tm_gmt.tm_min), s : DEC2BCD(tm_gmt.tm_sec), offset : htons(tm_loc.tm_gmtoff / 60) }; 00851 bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer; 00852 DumpTPDUDataTransfer &= DumpDateTime; 00853 if (DumpDateTime) 00854 dbgprotocol("Slot %d: ==> Date Time (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 00855 SendData(AOT_DATE_TIME, 7, (uint8_t*)&T); 00856 DumpTPDUDataTransfer = OldDumpTPDUDataTransfer; 00857 } 00858 } 00859 00860 void cCiDateTime::Process(int Length, const uint8_t *Data) 00861 { 00862 if (Data) { 00863 int Tag = GetTag(Length, &Data); 00864 switch (Tag) { 00865 case AOT_DATE_TIME_ENQ: { 00866 interval = 0; 00867 int l = 0; 00868 const uint8_t *d = GetData(Data, l); 00869 if (l > 0) 00870 interval = *d; 00871 dbgprotocol("Slot %d: <== Date Time Enq (%d), interval = %d\n", Tc()->CamSlot()->SlotNumber(), SessionId(), interval); 00872 lastTime = time(NULL); 00873 SendDateTime(); 00874 } 00875 break; 00876 default: esyslog("ERROR: CAM %d: date time: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag); 00877 } 00878 } 00879 else if (interval && time(NULL) - lastTime > interval) { 00880 lastTime = time(NULL); 00881 SendDateTime(); 00882 } 00883 } 00884 00885 // --- cCiMMI ---------------------------------------------------------------- 00886 00887 // Display Control Commands: 00888 00889 #define DCC_SET_MMI_MODE 0x01 00890 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02 00891 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03 00892 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04 00893 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05 00894 00895 // MMI Modes: 00896 00897 #define MM_HIGH_LEVEL 0x01 00898 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02 00899 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03 00900 00901 // Display Reply IDs: 00902 00903 #define DRI_MMI_MODE_ACK 0x01 00904 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02 00905 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03 00906 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04 00907 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05 00908 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0 00909 #define DRI_UNKNOWN_MMI_MODE 0xF1 00910 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2 00911 00912 // Enquiry Flags: 00913 00914 #define EF_BLIND 0x01 00915 00916 // Answer IDs: 00917 00918 #define AI_CANCEL 0x00 00919 #define AI_ANSWER 0x01 00920 00921 class cCiMMI : public cCiSession { 00922 private: 00923 char *GetText(int &Length, const uint8_t **Data); 00924 cCiMenu *menu, *fetchedMenu; 00925 cCiEnquiry *enquiry, *fetchedEnquiry; 00926 public: 00927 cCiMMI(uint16_t SessionId, cCiTransportConnection *Tc); 00928 virtual ~cCiMMI(); 00929 virtual void Process(int Length = 0, const uint8_t *Data = NULL); 00930 virtual bool HasUserIO(void) { return menu || enquiry; } 00931 cCiMenu *Menu(bool Clear = false); 00932 cCiEnquiry *Enquiry(bool Clear = false); 00933 void SendMenuAnswer(uint8_t Selection); 00934 bool SendAnswer(const char *Text); 00935 bool SendCloseMMI(void); 00936 }; 00937 00938 cCiMMI::cCiMMI(uint16_t SessionId, cCiTransportConnection *Tc) 00939 :cCiSession(SessionId, RI_MMI, Tc) 00940 { 00941 dbgprotocol("Slot %d: new MMI (session id %d)\n", Tc->CamSlot()->SlotNumber(), SessionId); 00942 menu = fetchedMenu = NULL; 00943 enquiry = fetchedEnquiry = NULL; 00944 } 00945 00946 cCiMMI::~cCiMMI() 00947 { 00948 if (fetchedMenu) { 00949 cMutexLock MutexLock(fetchedMenu->mutex); 00950 fetchedMenu->mmi = NULL; 00951 } 00952 delete menu; 00953 if (fetchedEnquiry) { 00954 cMutexLock MutexLock(fetchedEnquiry->mutex); 00955 fetchedEnquiry->mmi = NULL; 00956 } 00957 delete enquiry; 00958 } 00959 00960 char *cCiMMI::GetText(int &Length, const uint8_t **Data) 00964 { 00965 int Tag = GetTag(Length, Data); 00966 if (Tag == AOT_TEXT_LAST) { 00967 char *s = GetString(Length, Data); 00968 dbgprotocol("Slot %d: <== Text Last (%d) '%s'\n", Tc()->CamSlot()->SlotNumber(), SessionId(), s); 00969 return s; 00970 } 00971 else 00972 esyslog("ERROR: CAM %d: MMI: unexpected text tag: %06X", Tc()->CamSlot()->SlotNumber(), Tag); 00973 return NULL; 00974 } 00975 00976 void cCiMMI::Process(int Length, const uint8_t *Data) 00977 { 00978 if (Data) { 00979 int Tag = GetTag(Length, &Data); 00980 switch (Tag) { 00981 case AOT_DISPLAY_CONTROL: { 00982 dbgprotocol("Slot %d: <== Display Control (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 00983 int l = 0; 00984 const uint8_t *d = GetData(Data, l); 00985 if (l > 0) { 00986 switch (*d) { 00987 case DCC_SET_MMI_MODE: 00988 if (l == 2 && *++d == MM_HIGH_LEVEL) { 00989 struct tDisplayReply { uint8_t id; uint8_t mode; }; 00990 tDisplayReply dr = { id : DRI_MMI_MODE_ACK, mode : MM_HIGH_LEVEL }; 00991 dbgprotocol("Slot %d: ==> Display Reply (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 00992 SendData(AOT_DISPLAY_REPLY, 2, (uint8_t *)&dr); 00993 } 00994 break; 00995 default: esyslog("ERROR: CAM %d: MMI: unsupported display control command %02X", Tc()->CamSlot()->SlotNumber(), *d); 00996 } 00997 } 00998 } 00999 break; 01000 case AOT_LIST_LAST: 01001 case AOT_MENU_LAST: { 01002 dbgprotocol("Slot %d: <== Menu Last (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 01003 delete menu; 01004 menu = new cCiMenu(this, Tag == AOT_MENU_LAST); 01005 int l = 0; 01006 const uint8_t *d = GetData(Data, l); 01007 if (l > 0) { 01008 // since the specification allows choiceNb to be undefined it is useless, so let's just skip it: 01009 d++; 01010 l--; 01011 if (l > 0) menu->titleText = GetText(l, &d); 01012 if (l > 0) menu->subTitleText = GetText(l, &d); 01013 if (l > 0) menu->bottomText = GetText(l, &d); 01014 while (l > 0) { 01015 char *s = GetText(l, &d); 01016 if (s) { 01017 if (!menu->AddEntry(s)) 01018 free(s); 01019 } 01020 else 01021 break; 01022 } 01023 } 01024 } 01025 break; 01026 case AOT_ENQ: { 01027 dbgprotocol("Slot %d: <== Enq (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 01028 delete enquiry; 01029 enquiry = new cCiEnquiry(this); 01030 int l = 0; 01031 const uint8_t *d = GetData(Data, l); 01032 if (l > 0) { 01033 uint8_t blind = *d++; 01034 //XXX GetByte()??? 01035 l--; 01036 enquiry->blind = blind & EF_BLIND; 01037 enquiry->expectedLength = *d++; 01038 l--; 01039 // I really wonder why there is no text length field here... 01040 enquiry->text = CopyString(l, d); 01041 } 01042 } 01043 break; 01044 case AOT_CLOSE_MMI: { 01045 int id = -1; 01046 int delay = -1; 01047 int l = 0; 01048 const uint8_t *d = GetData(Data, l); 01049 if (l > 0) { 01050 id = *d++; 01051 if (l > 1) 01052 delay = *d; 01053 } 01054 dbgprotocol("Slot %d: <== Close MMI (%d) id = %02X delay = %d\n", Tc()->CamSlot()->SlotNumber(), SessionId(), id, delay); 01055 } 01056 break; 01057 default: esyslog("ERROR: CAM %d: MMI: unknown tag %06X", Tc()->CamSlot()->SlotNumber(), Tag); 01058 } 01059 } 01060 } 01061 01062 cCiMenu *cCiMMI::Menu(bool Clear) 01063 { 01064 if (Clear) 01065 fetchedMenu = NULL; 01066 else if (menu) { 01067 fetchedMenu = menu; 01068 menu = NULL; 01069 } 01070 return fetchedMenu; 01071 } 01072 01073 cCiEnquiry *cCiMMI::Enquiry(bool Clear) 01074 { 01075 if (Clear) 01076 fetchedEnquiry = NULL; 01077 else if (enquiry) { 01078 fetchedEnquiry = enquiry; 01079 enquiry = NULL; 01080 } 01081 return fetchedEnquiry; 01082 } 01083 01084 void cCiMMI::SendMenuAnswer(uint8_t Selection) 01085 { 01086 dbgprotocol("Slot %d: ==> Menu Answ (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 01087 SendData(AOT_MENU_ANSW, 1, &Selection); 01088 } 01089 01090 bool cCiMMI::SendAnswer(const char *Text) 01091 { 01092 dbgprotocol("Slot %d: ==> Answ (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 01093 struct tAnswer { uint8_t id; char text[256]; };//XXX 01094 tAnswer answer; 01095 answer.id = Text ? AI_ANSWER : AI_CANCEL; 01096 if (Text) 01097 strncpy(answer.text, Text, sizeof(answer.text)); 01098 SendData(AOT_ANSW, Text ? strlen(Text) + 1 : 1, (uint8_t *)&answer); 01099 return true; 01100 } 01101 01102 bool cCiMMI::SendCloseMMI(void) 01103 { 01104 dbgprotocol("Slot %d: ==> Close MMI (%d)\n", Tc()->CamSlot()->SlotNumber(), SessionId()); 01105 SendData(AOT_CLOSE_MMI, 0); 01106 return true; 01107 } 01108 01109 // --- cCiMenu --------------------------------------------------------------- 01110 01111 cCiMenu::cCiMenu(cCiMMI *MMI, bool Selectable) 01112 { 01113 mmi = MMI; 01114 mutex = NULL; 01115 selectable = Selectable; 01116 titleText = subTitleText = bottomText = NULL; 01117 numEntries = 0; 01118 } 01119 01120 cCiMenu::~cCiMenu() 01121 { 01122 cMutexLock MutexLock(mutex); 01123 if (mmi) 01124 mmi->Menu(true); 01125 free(titleText); 01126 free(subTitleText); 01127 free(bottomText); 01128 for (int i = 0; i < numEntries; i++) 01129 free(entries[i]); 01130 } 01131 01132 bool cCiMenu::AddEntry(char *s) 01133 { 01134 if (numEntries < MAX_CIMENU_ENTRIES) { 01135 entries[numEntries++] = s; 01136 return true; 01137 } 01138 return false; 01139 } 01140 01141 bool cCiMenu::HasUpdate(void) 01142 { 01143 // If the mmi is gone, the menu shall be closed, which also qualifies as 'update'. 01144 return !mmi || mmi->HasUserIO(); 01145 } 01146 01147 void cCiMenu::Select(int Index) 01148 { 01149 cMutexLock MutexLock(mutex); 01150 if (mmi && -1 <= Index && Index < numEntries) 01151 mmi->SendMenuAnswer(Index + 1); 01152 } 01153 01154 void cCiMenu::Cancel(void) 01155 { 01156 Select(-1); 01157 } 01158 01159 void cCiMenu::Abort(void) 01160 { 01161 cMutexLock MutexLock(mutex); 01162 if (mmi) 01163 mmi->SendCloseMMI(); 01164 } 01165 01166 // --- cCiEnquiry ------------------------------------------------------------ 01167 01168 cCiEnquiry::cCiEnquiry(cCiMMI *MMI) 01169 { 01170 mmi = MMI; 01171 text = NULL; 01172 blind = false; 01173 expectedLength = 0; 01174 } 01175 01176 cCiEnquiry::~cCiEnquiry() 01177 { 01178 cMutexLock MutexLock(mutex); 01179 if (mmi) 01180 mmi->Enquiry(true); 01181 free(text); 01182 } 01183 01184 void cCiEnquiry::Reply(const char *s) 01185 { 01186 cMutexLock MutexLock(mutex); 01187 if (mmi) 01188 mmi->SendAnswer(s); 01189 } 01190 01191 void cCiEnquiry::Cancel(void) 01192 { 01193 Reply(NULL); 01194 } 01195 01196 void cCiEnquiry::Abort(void) 01197 { 01198 cMutexLock MutexLock(mutex); 01199 if (mmi) 01200 mmi->SendCloseMMI(); 01201 } 01202 01203 // --- cCiTransportConnection (cont'd) --------------------------------------- 01204 01205 #define TC_POLL_TIMEOUT 300 // ms WORKAROUND: TC_POLL_TIMEOUT < 300ms doesn't work with DragonCAM 01206 #define TC_ALIVE_TIMEOUT 2000 // ms after which a transport connection is assumed dead 01207 01208 cCiTransportConnection::cCiTransportConnection(cCamSlot *CamSlot, uint8_t Tcid) 01209 { 01210 dbgprotocol("Slot %d: creating connection %d/%d\n", CamSlot->SlotNumber(), CamSlot->SlotIndex(), Tcid); 01211 camSlot = CamSlot; 01212 tcid = Tcid; 01213 state = stIDLE; 01214 createConnectionRequested = false; 01215 deleteConnectionRequested = false; 01216 hasUserIO = false; 01217 alive.Set(TC_ALIVE_TIMEOUT); 01218 for (int i = 0; i <= MAX_SESSIONS_PER_TC; i++) // sessions[0] is not used, but initialized anyway 01219 sessions[i] = NULL; 01220 } 01221 01222 cCiTransportConnection::~cCiTransportConnection() 01223 { 01224 for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) 01225 delete sessions[i]; 01226 } 01227 01228 bool cCiTransportConnection::Ready(void) 01229 { 01230 cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT); 01231 return cas && cas->Ready(); 01232 } 01233 01234 const char *cCiTransportConnection::GetCamName(void) 01235 { 01236 cCiApplicationInformation *ai = (cCiApplicationInformation *)GetSessionByResourceId(RI_APPLICATION_INFORMATION); 01237 return ai ? ai->GetMenuString() : NULL; 01238 } 01239 01240 void cCiTransportConnection::SendTPDU(uint8_t Tag, int Length, const uint8_t *Data) 01241 { 01242 cTPDU TPDU(camSlot->SlotIndex(), tcid, Tag, Length, Data); 01243 camSlot->Write(&TPDU); 01244 timer.Set(TC_POLL_TIMEOUT); 01245 } 01246 01247 void cCiTransportConnection::SendData(int Length, const uint8_t *Data) 01248 { 01249 // if Length ever exceeds MAX_TPDU_DATA this needs to be handled differently 01250 if (state == stACTIVE && Length > 0) 01251 SendTPDU(T_DATA_LAST, Length, Data); 01252 } 01253 01254 void cCiTransportConnection::SendTag(uint8_t Tag, uint16_t SessionId, uint32_t ResourceId, int Status) 01255 { 01256 uint8_t buffer[16]; 01257 uint8_t *p = buffer; 01258 *p++ = Tag; 01259 *p++ = 0x00; // will contain length 01260 if (Status >= 0) 01261 *p++ = Status; 01262 if (ResourceId) { 01263 put_unaligned(htonl(ResourceId), (uint32_t *)p); 01264 p += 4; 01265 } 01266 put_unaligned(htons(SessionId), (uint16_t *)p); 01267 p += 2; 01268 buffer[1] = p - buffer - 2; // length 01269 SendData(p - buffer, buffer); 01270 } 01271 01272 void cCiTransportConnection::Poll(void) 01273 { 01274 bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer; 01275 DumpTPDUDataTransfer &= DumpPolls; 01276 if (DumpPolls) 01277 dbgprotocol("Slot %d: ==> Poll\n", camSlot->SlotNumber()); 01278 SendTPDU(T_DATA_LAST); 01279 DumpTPDUDataTransfer = OldDumpTPDUDataTransfer; 01280 } 01281 01282 uint32_t cCiTransportConnection::ResourceIdToInt(const uint8_t *Data) 01283 { 01284 return (ntohl(get_unaligned((uint32_t *)Data))); 01285 } 01286 01287 cCiSession *cCiTransportConnection::GetSessionBySessionId(uint16_t SessionId) 01288 { 01289 return (SessionId <= MAX_SESSIONS_PER_TC) ? sessions[SessionId] : NULL; 01290 } 01291 01292 cCiSession *cCiTransportConnection::GetSessionByResourceId(uint32_t ResourceId) 01293 { 01294 for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) { 01295 if (sessions[i] && sessions[i]->ResourceId() == ResourceId) 01296 return sessions[i]; 01297 } 01298 return NULL; 01299 } 01300 01301 void cCiTransportConnection::OpenSession(int Length, const uint8_t *Data) 01302 { 01303 if (Length == 6 && *(Data + 1) == 0x04) { 01304 uint32_t ResourceId = ResourceIdToInt(Data + 2); 01305 dbgprotocol("Slot %d: open session %08X\n", camSlot->SlotNumber(), ResourceId); 01306 if (!GetSessionByResourceId(ResourceId)) { 01307 for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) { 01308 if (!sessions[i]) { 01309 switch (ResourceId) { 01310 case RI_RESOURCE_MANAGER: sessions[i] = new cCiResourceManager(i, this); break; 01311 case RI_APPLICATION_INFORMATION: sessions[i] = new cCiApplicationInformation(i, this); break; 01312 case RI_CONDITIONAL_ACCESS_SUPPORT: sessions[i] = new cCiConditionalAccessSupport(i, this); break; 01313 case RI_DATE_TIME: sessions[i] = new cCiDateTime(i, this); break; 01314 case RI_MMI: sessions[i] = new cCiMMI(i, this); break; 01315 case RI_HOST_CONTROL: // not implemented 01316 default: esyslog("ERROR: CAM %d: unknown resource identifier: %08X (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid); 01317 } 01318 if (sessions[i]) 01319 SendTag(ST_OPEN_SESSION_RESPONSE, sessions[i]->SessionId(), sessions[i]->ResourceId(), SS_OK); 01320 return; 01321 } 01322 } 01323 esyslog("ERROR: CAM %d: no free session slot for resource identifier %08X (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid); 01324 } 01325 else 01326 esyslog("ERROR: CAM %d: session for resource identifier %08X already exists (%d/%d)", camSlot->SlotNumber(), ResourceId, camSlot->SlotIndex(), tcid); 01327 } 01328 } 01329 01330 void cCiTransportConnection::CloseSession(uint16_t SessionId) 01331 { 01332 dbgprotocol("Slot %d: close session %d\n", camSlot->SlotNumber(), SessionId); 01333 cCiSession *Session = GetSessionBySessionId(SessionId); 01334 if (Session && sessions[SessionId] == Session) { 01335 delete Session; 01336 sessions[SessionId] = NULL; 01337 SendTag(ST_CLOSE_SESSION_RESPONSE, SessionId, 0, SS_OK); 01338 } 01339 else { 01340 esyslog("ERROR: CAM %d: unknown session id: %d (%d/%d)", camSlot->SlotNumber(), SessionId, camSlot->SlotIndex(), tcid); 01341 SendTag(ST_CLOSE_SESSION_RESPONSE, SessionId, 0, SS_NOT_ALLOCATED); 01342 } 01343 } 01344 01345 void cCiTransportConnection::HandleSessions(cTPDU *TPDU) 01346 { 01347 int Length; 01348 const uint8_t *Data = TPDU->Data(Length); 01349 if (Data && Length > 1) { 01350 switch (*Data) { 01351 case ST_SESSION_NUMBER: if (Length > 4) { 01352 uint16_t SessionId = ntohs(get_unaligned((uint16_t *)&Data[2])); 01353 cCiSession *Session = GetSessionBySessionId(SessionId); 01354 if (Session) 01355 Session->Process(Length - 4, Data + 4); 01356 else 01357 esyslog("ERROR: CAM %d: unknown session id: %d (%d/%d)", camSlot->SlotNumber(), SessionId, camSlot->SlotIndex(), tcid); 01358 } 01359 break; 01360 case ST_OPEN_SESSION_REQUEST: OpenSession(Length, Data); 01361 break; 01362 case ST_CLOSE_SESSION_REQUEST: if (Length == 4) 01363 CloseSession(ntohs(get_unaligned((uint16_t *)&Data[2]))); 01364 break; 01365 case ST_CREATE_SESSION_RESPONSE: // not implemented 01366 case ST_CLOSE_SESSION_RESPONSE: // not implemented 01367 default: esyslog("ERROR: CAM %d: unknown session tag: %02X (%d/%d)", camSlot->SlotNumber(), *Data, camSlot->SlotIndex(), tcid); 01368 } 01369 } 01370 } 01371 01372 bool cCiTransportConnection::Process(cTPDU *TPDU) 01373 { 01374 if (TPDU) 01375 alive.Set(TC_ALIVE_TIMEOUT); 01376 else if (alive.TimedOut()) 01377 return false; 01378 switch (state) { 01379 case stIDLE: 01380 if (createConnectionRequested) { 01381 dbgprotocol("Slot %d: create connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid); 01382 createConnectionRequested = false; 01383 SendTPDU(T_CREATE_TC); 01384 state = stCREATION; 01385 } 01386 return true; 01387 case stCREATION: 01388 if (TPDU && TPDU->Tag() == T_CTC_REPLY) { 01389 dbgprotocol("Slot %d: connection created %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid); 01390 Poll(); 01391 state = stACTIVE; 01392 } 01393 else if (timer.TimedOut()) { 01394 dbgprotocol("Slot %d: timeout while creating connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid); 01395 state = stIDLE; 01396 } 01397 return true; 01398 case stACTIVE: 01399 if (deleteConnectionRequested) { 01400 dbgprotocol("Slot %d: delete connection requested %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid); 01401 deleteConnectionRequested = false; 01402 SendTPDU(T_DELETE_TC); 01403 state = stDELETION; 01404 return true; 01405 } 01406 if (TPDU) { 01407 switch (TPDU->Tag()) { 01408 case T_REQUEST_TC: 01409 esyslog("ERROR: CAM %d: T_REQUEST_TC not implemented (%d/%d)", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid); 01410 break; 01411 case T_DATA_MORE: 01412 case T_DATA_LAST: 01413 HandleSessions(TPDU); 01414 // continue with T_SB 01415 case T_SB: 01416 if ((TPDU->Status() & DATA_INDICATOR) != 0) { 01417 dbgprotocol("Slot %d: receive data %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid); 01418 SendTPDU(T_RCV); 01419 } 01420 break; 01421 case T_DELETE_TC: 01422 dbgprotocol("Slot %d: delete connection %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid); 01423 SendTPDU(T_DTC_REPLY); 01424 state = stIDLE; 01425 return true; 01426 case T_RCV: 01427 case T_CREATE_TC: 01428 case T_CTC_REPLY: 01429 case T_DTC_REPLY: 01430 case T_NEW_TC: 01431 case T_TC_ERROR: 01432 break; 01433 default: 01434 esyslog("ERROR: unknown TPDU tag: 0x%02X (%s)", TPDU->Tag(), __FUNCTION__); 01435 } 01436 } 01437 else if (timer.TimedOut()) 01438 Poll(); 01439 hasUserIO = false; 01440 for (int i = 1; i <= MAX_SESSIONS_PER_TC; i++) { 01441 if (sessions[i]) { 01442 sessions[i]->Process(); 01443 if (sessions[i]->HasUserIO()) 01444 hasUserIO = true; 01445 } 01446 } 01447 break; 01448 case stDELETION: 01449 if (TPDU && TPDU->Tag() == T_DTC_REPLY || timer.TimedOut()) { 01450 dbgprotocol("Slot %d: connection deleted %d/%d\n", camSlot->SlotNumber(), camSlot->SlotIndex(), tcid); 01451 state = stIDLE; 01452 } 01453 return true; 01454 default: 01455 esyslog("ERROR: unknown state: %d (%s)", state, __FUNCTION__); 01456 } 01457 return true; 01458 } 01459 01460 // --- cCiCaPidData ---------------------------------------------------------- 01461 01462 class cCiCaPidData : public cListObject { 01463 public: 01464 bool active; 01465 int pid; 01466 int streamType; 01467 cCiCaPidData(int Pid, int StreamType) 01468 { 01469 active = false; 01470 pid = Pid; 01471 streamType = StreamType; 01472 } 01473 }; 01474 01475 // --- cCiCaProgramData ------------------------------------------------------ 01476 01477 class cCiCaProgramData : public cListObject { 01478 public: 01479 int programNumber; 01480 bool modified; 01481 cList<cCiCaPidData> pidList; 01482 cCiCaProgramData(int ProgramNumber) 01483 { 01484 programNumber = ProgramNumber; 01485 modified = false; 01486 } 01487 }; 01488 01489 // --- cCiAdapter ------------------------------------------------------------ 01490 01491 cCiAdapter::cCiAdapter(void) 01492 :cThread("CI adapter") 01493 { 01494 assignedDevice = NULL; 01495 for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) 01496 camSlots[i] = NULL; 01497 } 01498 01499 cCiAdapter::~cCiAdapter() 01500 { 01501 Cancel(3); 01502 for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) 01503 delete camSlots[i]; 01504 } 01505 01506 void cCiAdapter::AddCamSlot(cCamSlot *CamSlot) 01507 { 01508 if (CamSlot) { 01509 for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) { 01510 if (!camSlots[i]) { 01511 CamSlot->slotIndex = i; 01512 camSlots[i] = CamSlot; 01513 return; 01514 } 01515 } 01516 esyslog("ERROR: no free CAM slot in CI adapter"); 01517 } 01518 } 01519 01520 bool cCiAdapter::Ready(void) 01521 { 01522 for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) { 01523 if (camSlots[i] && !camSlots[i]->Ready()) 01524 return false; 01525 } 01526 return true; 01527 } 01528 01529 void cCiAdapter::Action(void) 01530 { 01531 cTPDU TPDU; 01532 while (Running()) { 01533 int n = Read(TPDU.Buffer(), TPDU.MaxSize()); 01534 if (n > 0 && TPDU.Slot() < MAX_CAM_SLOTS_PER_ADAPTER) { 01535 TPDU.SetSize(n); 01536 cCamSlot *cs = camSlots[TPDU.Slot()]; 01537 TPDU.Dump(cs ? cs->SlotNumber() : 0, false); 01538 if (cs) 01539 cs->Process(&TPDU); 01540 } 01541 for (int i = 0; i < MAX_CAM_SLOTS_PER_ADAPTER; i++) { 01542 if (camSlots[i]) 01543 camSlots[i]->Process(); 01544 } 01545 } 01546 } 01547 01548 // --- cCamSlot -------------------------------------------------------------- 01549 01550 cCamSlots CamSlots; 01551 01552 #define MODULE_CHECK_INTERVAL 500 // ms 01553 #define MODULE_RESET_TIMEOUT 2 // s 01554 01555 cCamSlot::cCamSlot(cCiAdapter *CiAdapter) 01556 { 01557 ciAdapter = CiAdapter; 01558 slotIndex = -1; 01559 lastModuleStatus = msReset; // avoids initial reset log message 01560 resetTime = 0; 01561 resendPmt = false; 01562 source = transponder = 0; 01563 for (int i = 0; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) // tc[0] is not used, but initialized anyway 01564 tc[i] = NULL; 01565 CamSlots.Add(this); 01566 slotNumber = Index() + 1; 01567 if (ciAdapter) 01568 ciAdapter->AddCamSlot(this); 01569 Reset(); 01570 } 01571 01572 cCamSlot::~cCamSlot() 01573 { 01574 CamSlots.Del(this, false); 01575 DeleteAllConnections(); 01576 } 01577 01578 bool cCamSlot::Assign(cDevice *Device, bool Query) 01579 { 01580 cMutexLock MutexLock(&mutex); 01581 if (ciAdapter) { 01582 if (ciAdapter->Assign(Device, true)) { 01583 if (!Device && ciAdapter->assignedDevice) 01584 ciAdapter->assignedDevice->SetCamSlot(NULL); 01585 if (!Query) { 01586 StopDecrypting(); 01587 source = transponder = 0; 01588 if (ciAdapter->Assign(Device)) { 01589 ciAdapter->assignedDevice = Device; 01590 if (Device) { 01591 Device->SetCamSlot(this); 01592 dsyslog("CAM %d: assigned to device %d", slotNumber, Device->DeviceNumber() + 1); 01593 } 01594 else 01595 dsyslog("CAM %d: unassigned", slotNumber); 01596 } 01597 else 01598 return false; 01599 } 01600 return true; 01601 } 01602 } 01603 return false; 01604 } 01605 01606 cDevice *cCamSlot::Device(void) 01607 { 01608 cMutexLock MutexLock(&mutex); 01609 if (ciAdapter) { 01610 cDevice *d = ciAdapter->assignedDevice; 01611 if (d && d->CamSlot() == this) 01612 return d; 01613 } 01614 return NULL; 01615 } 01616 01617 void cCamSlot::NewConnection(void) 01618 { 01619 cMutexLock MutexLock(&mutex); 01620 for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) { 01621 if (!tc[i]) { 01622 tc[i] = new cCiTransportConnection(this, i); 01623 tc[i]->CreateConnection(); 01624 return; 01625 } 01626 } 01627 esyslog("ERROR: CAM %d: can't create new transport connection!", slotNumber); 01628 } 01629 01630 void cCamSlot::DeleteAllConnections(void) 01631 { 01632 cMutexLock MutexLock(&mutex); 01633 for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) { 01634 delete tc[i]; 01635 tc[i] = NULL; 01636 } 01637 } 01638 01639 void cCamSlot::Process(cTPDU *TPDU) 01640 { 01641 cMutexLock MutexLock(&mutex); 01642 if (TPDU) { 01643 int n = TPDU->Tcid(); 01644 if (1 <= n && n <= MAX_CONNECTIONS_PER_CAM_SLOT) { 01645 if (tc[n]) 01646 tc[n]->Process(TPDU); 01647 } 01648 } 01649 for (int i = 1; i <= MAX_CONNECTIONS_PER_CAM_SLOT; i++) { 01650 if (tc[i]) { 01651 if (!tc[i]->Process()) { 01652 Reset(); 01653 return; 01654 } 01655 } 01656 } 01657 if (moduleCheckTimer.TimedOut()) { 01658 eModuleStatus ms = ModuleStatus(); 01659 if (ms != lastModuleStatus) { 01660 switch (ms) { 01661 case msNone: 01662 dbgprotocol("Slot %d: no module present\n", slotNumber); 01663 isyslog("CAM %d: no module present", slotNumber); 01664 DeleteAllConnections(); 01665 break; 01666 case msReset: 01667 dbgprotocol("Slot %d: module reset\n", slotNumber); 01668 isyslog("CAM %d: module reset", slotNumber); 01669 DeleteAllConnections(); 01670 break; 01671 case msPresent: 01672 dbgprotocol("Slot %d: module present\n", slotNumber); 01673 isyslog("CAM %d: module present", slotNumber); 01674 break; 01675 case msReady: 01676 dbgprotocol("Slot %d: module ready\n", slotNumber); 01677 isyslog("CAM %d: module ready", slotNumber); 01678 NewConnection(); 01679 resendPmt = caProgramList.Count() > 0; 01680 break; 01681 default: 01682 esyslog("ERROR: unknown module status %d (%s)", ms, __FUNCTION__); 01683 } 01684 lastModuleStatus = ms; 01685 } 01686 moduleCheckTimer.Set(MODULE_CHECK_INTERVAL); 01687 } 01688 if (resendPmt) 01689 SendCaPmt(CPCI_OK_DESCRAMBLING); 01690 processed.Broadcast(); 01691 } 01692 01693 cCiSession *cCamSlot::GetSessionByResourceId(uint32_t ResourceId) 01694 { 01695 cMutexLock MutexLock(&mutex); 01696 return tc[1] ? tc[1]->GetSessionByResourceId(ResourceId) : NULL; 01697 } 01698 01699 void cCamSlot::Write(cTPDU *TPDU) 01700 { 01701 cMutexLock MutexLock(&mutex); 01702 if (ciAdapter && TPDU->Size()) { 01703 TPDU->Dump(SlotNumber(), true); 01704 ciAdapter->Write(TPDU->Buffer(), TPDU->Size()); 01705 } 01706 } 01707 01708 bool cCamSlot::Reset(void) 01709 { 01710 cMutexLock MutexLock(&mutex); 01711 ChannelCamRelations.Reset(slotNumber); 01712 DeleteAllConnections(); 01713 if (ciAdapter) { 01714 dbgprotocol("Slot %d: reset...", slotNumber); 01715 if (ciAdapter->Reset(slotIndex)) { 01716 resetTime = time(NULL); 01717 dbgprotocol("ok.\n"); 01718 return true; 01719 } 01720 dbgprotocol("failed!\n"); 01721 } 01722 return false; 01723 } 01724 01725 eModuleStatus cCamSlot::ModuleStatus(void) 01726 { 01727 cMutexLock MutexLock(&mutex); 01728 eModuleStatus ms = ciAdapter ? ciAdapter->ModuleStatus(slotIndex) : msNone; 01729 if (resetTime) { 01730 if (ms <= msReset) { 01731 if (time(NULL) - resetTime < MODULE_RESET_TIMEOUT) 01732 return msReset; 01733 } 01734 resetTime = 0; 01735 } 01736 return ms; 01737 } 01738 01739 const char *cCamSlot::GetCamName(void) 01740 { 01741 cMutexLock MutexLock(&mutex); 01742 return tc[1] ? tc[1]->GetCamName() : NULL; 01743 } 01744 01745 bool cCamSlot::Ready(void) 01746 { 01747 cMutexLock MutexLock(&mutex); 01748 return ModuleStatus() == msNone || tc[1] && tc[1]->Ready(); 01749 } 01750 01751 bool cCamSlot::HasMMI(void) 01752 { 01753 return GetSessionByResourceId(RI_MMI); 01754 } 01755 01756 bool cCamSlot::HasUserIO(void) 01757 { 01758 cMutexLock MutexLock(&mutex); 01759 return tc[1] && tc[1]->HasUserIO(); 01760 } 01761 01762 bool cCamSlot::EnterMenu(void) 01763 { 01764 cMutexLock MutexLock(&mutex); 01765 cCiApplicationInformation *api = (cCiApplicationInformation *)GetSessionByResourceId(RI_APPLICATION_INFORMATION); 01766 return api ? api->EnterMenu() : false; 01767 } 01768 01769 cCiMenu *cCamSlot::GetMenu(void) 01770 { 01771 cMutexLock MutexLock(&mutex); 01772 cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI); 01773 if (mmi) { 01774 cCiMenu *Menu = mmi->Menu(); 01775 if (Menu) 01776 Menu->mutex = &mutex; 01777 return Menu; 01778 } 01779 return NULL; 01780 } 01781 01782 cCiEnquiry *cCamSlot::GetEnquiry(void) 01783 { 01784 cMutexLock MutexLock(&mutex); 01785 cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI); 01786 if (mmi) { 01787 cCiEnquiry *Enquiry = mmi->Enquiry(); 01788 if (Enquiry) 01789 Enquiry->mutex = &mutex; 01790 return Enquiry; 01791 } 01792 return NULL; 01793 } 01794 01795 void cCamSlot::SendCaPmt(uint8_t CmdId) 01796 { 01797 cMutexLock MutexLock(&mutex); 01798 cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT); 01799 if (cas) { 01800 const int *CaSystemIds = cas->GetCaSystemIds(); 01801 if (CaSystemIds && *CaSystemIds) { 01802 if (caProgramList.Count()) { 01803 for (int Loop = 1; Loop <= 2; Loop++) { 01804 for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) { 01805 if (p->modified || resendPmt) { 01806 bool Active = false; 01807 cCiCaPmt CaPmt(CmdId, source, transponder, p->programNumber, CaSystemIds); 01808 for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) { 01809 if (q->active) { 01810 CaPmt.AddPid(q->pid, q->streamType); 01811 Active = true; 01812 } 01813 } 01814 if ((Loop == 1) != Active) { // first remove, then add 01815 if (cas->RepliesToQuery()) 01816 CaPmt.SetListManagement(Active ? CPLM_ADD : CPLM_UPDATE); 01817 if (Active || cas->RepliesToQuery()) 01818 cas->SendPMT(&CaPmt); 01819 p->modified = false; 01820 } 01821 } 01822 } 01823 } 01824 resendPmt = false; 01825 } 01826 else { 01827 cCiCaPmt CaPmt(CmdId, 0, 0, 0, NULL); 01828 cas->SendPMT(&CaPmt); 01829 } 01830 } 01831 } 01832 } 01833 01834 const int *cCamSlot::GetCaSystemIds(void) 01835 { 01836 cMutexLock MutexLock(&mutex); 01837 cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT); 01838 return cas ? cas->GetCaSystemIds() : NULL; 01839 } 01840 01841 int cCamSlot::Priority(void) 01842 { 01843 cDevice *d = Device(); 01844 return d ? d->Priority() : IDLEPRIORITY; 01845 } 01846 01847 bool cCamSlot::ProvidesCa(const int *CaSystemIds) 01848 { 01849 cMutexLock MutexLock(&mutex); 01850 cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT); 01851 if (cas) { 01852 for (const int *ids = cas->GetCaSystemIds(); ids && *ids; ids++) { 01853 for (const int *id = CaSystemIds; *id; id++) { 01854 if (*id == *ids) 01855 return true; 01856 } 01857 } 01858 } 01859 return false; 01860 } 01861 01862 void cCamSlot::AddPid(int ProgramNumber, int Pid, int StreamType) 01863 { 01864 cMutexLock MutexLock(&mutex); 01865 cCiCaProgramData *ProgramData = NULL; 01866 for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) { 01867 if (p->programNumber == ProgramNumber) { 01868 ProgramData = p; 01869 for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) { 01870 if (q->pid == Pid) 01871 return; 01872 } 01873 } 01874 } 01875 if (!ProgramData) 01876 caProgramList.Add(ProgramData = new cCiCaProgramData(ProgramNumber)); 01877 ProgramData->pidList.Add(new cCiCaPidData(Pid, StreamType)); 01878 } 01879 01880 void cCamSlot::SetPid(int Pid, bool Active) 01881 { 01882 cMutexLock MutexLock(&mutex); 01883 for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) { 01884 for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) { 01885 if (q->pid == Pid) { 01886 if (q->active != Active) { 01887 q->active = Active; 01888 p->modified = true; 01889 } 01890 return; 01891 } 01892 } 01893 } 01894 } 01895 01896 // see ISO/IEC 13818-1 01897 #define STREAM_TYPE_VIDEO 0x02 01898 #define STREAM_TYPE_AUDIO 0x04 01899 #define STREAM_TYPE_PRIVATE 0x06 01900 01901 void cCamSlot::AddChannel(const cChannel *Channel) 01902 { 01903 cMutexLock MutexLock(&mutex); 01904 if (source != Channel->Source() || transponder != Channel->Transponder()) 01905 StopDecrypting(); 01906 source = Channel->Source(); 01907 transponder = Channel->Transponder(); 01908 if (Channel->Ca() >= CA_ENCRYPTED_MIN) { 01909 AddPid(Channel->Sid(), Channel->Vpid(), STREAM_TYPE_VIDEO); 01910 for (const int *Apid = Channel->Apids(); *Apid; Apid++) 01911 AddPid(Channel->Sid(), *Apid, STREAM_TYPE_AUDIO); 01912 for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++) 01913 AddPid(Channel->Sid(), *Dpid, STREAM_TYPE_PRIVATE); 01914 for (const int *Spid = Channel->Spids(); *Spid; Spid++) 01915 AddPid(Channel->Sid(), *Spid, STREAM_TYPE_PRIVATE); 01916 if (Channel->Tpid() && Setup.SupportTeletext) 01917 AddPid(Channel->Sid(), Channel->Tpid(), STREAM_TYPE_PRIVATE); 01918 } 01919 } 01920 01921 #define QUERY_REPLY_WAIT 100 // ms to wait between checks for a reply 01922 01923 bool cCamSlot::CanDecrypt(const cChannel *Channel) 01924 { 01925 if (Channel->Ca() < CA_ENCRYPTED_MIN) 01926 return true; // channel not encrypted 01927 if (!IsDecrypting()) 01928 return true; // any CAM can decrypt at least one channel 01929 cMutexLock MutexLock(&mutex); 01930 cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT); 01931 if (cas && cas->RepliesToQuery()) { 01932 cCiCaPmt CaPmt(CPCI_QUERY, Channel->Source(), Channel->Transponder(), Channel->Sid(), GetCaSystemIds()); 01933 CaPmt.SetListManagement(CPLM_ADD); // WORKAROUND: CPLM_ONLY doesn't work with Alphacrypt 3.09 (deletes existing CA_PMTs) 01934 CaPmt.AddPid(Channel->Vpid(), STREAM_TYPE_VIDEO); 01935 for (const int *Apid = Channel->Apids(); *Apid; Apid++) 01936 CaPmt.AddPid(*Apid, STREAM_TYPE_AUDIO); 01937 for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++) 01938 CaPmt.AddPid(*Dpid, STREAM_TYPE_PRIVATE); 01939 for (const int *Spid = Channel->Spids(); *Spid; Spid++) 01940 CaPmt.AddPid(*Spid, STREAM_TYPE_PRIVATE); 01941 if (Channel->Tpid() && Setup.SupportTeletext) { 01942 CaPmt.AddPid(Channel->Tpid(), STREAM_TYPE_PRIVATE); 01943 } 01944 cas->SendPMT(&CaPmt); 01945 cTimeMs Timeout(QUERY_REPLY_TIMEOUT); 01946 do { 01947 processed.TimedWait(mutex, QUERY_REPLY_WAIT); 01948 if ((cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT)) != NULL) { // must re-fetch it, there might have been a reset 01949 if (cas->ReceivedReply()) 01950 return cas->CanDecrypt(); 01951 } 01952 else 01953 return false; 01954 } while (!Timeout.TimedOut()); 01955 dsyslog("CAM %d: didn't reply to QUERY", SlotNumber()); 01956 } 01957 return false; 01958 } 01959 01960 void cCamSlot::StartDecrypting(void) 01961 { 01962 SendCaPmt(CPCI_OK_DESCRAMBLING); 01963 } 01964 01965 void cCamSlot::StopDecrypting(void) 01966 { 01967 cMutexLock MutexLock(&mutex); 01968 if (caProgramList.Count()) { 01969 caProgramList.Clear(); 01970 SendCaPmt(CPCI_NOT_SELECTED); 01971 } 01972 } 01973 01974 bool cCamSlot::IsDecrypting(void) 01975 { 01976 cMutexLock MutexLock(&mutex); 01977 if (caProgramList.Count()) { 01978 for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) { 01979 if (p->modified) 01980 return true; // any modifications need to be processed before we can assume it's no longer decrypting 01981 for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) { 01982 if (q->active) 01983 return true; 01984 } 01985 } 01986 } 01987 return false; 01988 } 01989 01990 // --- cChannelCamRelation --------------------------------------------------- 01991 01992 #define CAM_CHECKED_TIMEOUT 15 // seconds before a CAM that has been checked for a particular channel will be checked again 01993 01994 class cChannelCamRelation : public cListObject { 01995 private: 01996 tChannelID channelID; 01997 uint32_t camSlotsChecked; 01998 uint32_t camSlotsDecrypt; 01999 time_t lastChecked; 02000 public: 02001 cChannelCamRelation(tChannelID ChannelID); 02002 bool TimedOut(void); 02003 tChannelID ChannelID(void) { return channelID; } 02004 bool CamChecked(int CamSlotNumber); 02005 bool CamDecrypt(int CamSlotNumber); 02006 void SetChecked(int CamSlotNumber); 02007 void SetDecrypt(int CamSlotNumber); 02008 void ClrChecked(int CamSlotNumber); 02009 void ClrDecrypt(int CamSlotNumber); 02010 }; 02011 02012 cChannelCamRelation::cChannelCamRelation(tChannelID ChannelID) 02013 { 02014 channelID = ChannelID; 02015 camSlotsChecked = 0; 02016 camSlotsDecrypt = 0; 02017 lastChecked = 0; 02018 } 02019 02020 bool cChannelCamRelation::TimedOut(void) 02021 { 02022 return !camSlotsDecrypt && time(NULL) - lastChecked > CAM_CHECKED_TIMEOUT; 02023 } 02024 02025 bool cChannelCamRelation::CamChecked(int CamSlotNumber) 02026 { 02027 if (lastChecked && time(NULL) - lastChecked > CAM_CHECKED_TIMEOUT) { 02028 lastChecked = 0; 02029 camSlotsChecked = 0; 02030 } 02031 return camSlotsChecked & (1 << (CamSlotNumber - 1)); 02032 } 02033 02034 bool cChannelCamRelation::CamDecrypt(int CamSlotNumber) 02035 { 02036 return camSlotsDecrypt & (1 << (CamSlotNumber - 1)); 02037 } 02038 02039 void cChannelCamRelation::SetChecked(int CamSlotNumber) 02040 { 02041 camSlotsChecked |= (1 << (CamSlotNumber - 1)); 02042 lastChecked = time(NULL); 02043 ClrDecrypt(CamSlotNumber); 02044 } 02045 02046 void cChannelCamRelation::SetDecrypt(int CamSlotNumber) 02047 { 02048 camSlotsDecrypt |= (1 << (CamSlotNumber - 1)); 02049 ClrChecked(CamSlotNumber); 02050 } 02051 02052 void cChannelCamRelation::ClrChecked(int CamSlotNumber) 02053 { 02054 camSlotsChecked &= ~(1 << (CamSlotNumber - 1)); 02055 lastChecked = 0; 02056 } 02057 02058 void cChannelCamRelation::ClrDecrypt(int CamSlotNumber) 02059 { 02060 camSlotsDecrypt &= ~(1 << (CamSlotNumber - 1)); 02061 } 02062 02063 // --- cChannelCamRelations -------------------------------------------------- 02064 02065 #define CHANNEL_CAM_RELATIONS_CLEANUP_INTERVAL 3600 // seconds between cleanups 02066 02067 cChannelCamRelations ChannelCamRelations; 02068 02069 cChannelCamRelations::cChannelCamRelations(void) 02070 { 02071 lastCleanup = time(NULL); 02072 } 02073 02074 void cChannelCamRelations::Cleanup(void) 02075 { 02076 cMutexLock MutexLock(&mutex); 02077 if (time(NULL) - lastCleanup > CHANNEL_CAM_RELATIONS_CLEANUP_INTERVAL) { 02078 for (cChannelCamRelation *ccr = First(); ccr; ) { 02079 cChannelCamRelation *c = ccr; 02080 ccr = Next(ccr); 02081 if (c->TimedOut()) 02082 Del(c); 02083 } 02084 lastCleanup = time(NULL); 02085 } 02086 } 02087 02088 cChannelCamRelation *cChannelCamRelations::GetEntry(tChannelID ChannelID) 02089 { 02090 cMutexLock MutexLock(&mutex); 02091 Cleanup(); 02092 for (cChannelCamRelation *ccr = First(); ccr; ccr = Next(ccr)) { 02093 if (ccr->ChannelID() == ChannelID) 02094 return ccr; 02095 } 02096 return NULL; 02097 } 02098 02099 cChannelCamRelation *cChannelCamRelations::AddEntry(tChannelID ChannelID) 02100 { 02101 cMutexLock MutexLock(&mutex); 02102 cChannelCamRelation *ccr = GetEntry(ChannelID); 02103 if (!ccr) 02104 Add(ccr = new cChannelCamRelation(ChannelID)); 02105 return ccr; 02106 } 02107 02108 void cChannelCamRelations::Reset(int CamSlotNumber) 02109 { 02110 cMutexLock MutexLock(&mutex); 02111 for (cChannelCamRelation *ccr = First(); ccr; ccr = Next(ccr)) { 02112 ccr->ClrChecked(CamSlotNumber); 02113 ccr->ClrDecrypt(CamSlotNumber); 02114 } 02115 } 02116 02117 bool cChannelCamRelations::CamChecked(tChannelID ChannelID, int CamSlotNumber) 02118 { 02119 cMutexLock MutexLock(&mutex); 02120 cChannelCamRelation *ccr = GetEntry(ChannelID); 02121 return ccr ? ccr->CamChecked(CamSlotNumber) : false; 02122 } 02123 02124 bool cChannelCamRelations::CamDecrypt(tChannelID ChannelID, int CamSlotNumber) 02125 { 02126 cMutexLock MutexLock(&mutex); 02127 cChannelCamRelation *ccr = GetEntry(ChannelID); 02128 return ccr ? ccr->CamDecrypt(CamSlotNumber) : false; 02129 } 02130 02131 void cChannelCamRelations::SetChecked(tChannelID ChannelID, int CamSlotNumber) 02132 { 02133 cMutexLock MutexLock(&mutex); 02134 cChannelCamRelation *ccr = AddEntry(ChannelID); 02135 if (ccr) 02136 ccr->SetChecked(CamSlotNumber); 02137 } 02138 02139 void cChannelCamRelations::SetDecrypt(tChannelID ChannelID, int CamSlotNumber) 02140 { 02141 cMutexLock MutexLock(&mutex); 02142 cChannelCamRelation *ccr = AddEntry(ChannelID); 02143 if (ccr) 02144 ccr->SetDecrypt(CamSlotNumber); 02145 } 02146 02147 void cChannelCamRelations::ClrChecked(tChannelID ChannelID, int CamSlotNumber) 02148 { 02149 cMutexLock MutexLock(&mutex); 02150 cChannelCamRelation *ccr = GetEntry(ChannelID); 02151 if (ccr) 02152 ccr->ClrChecked(CamSlotNumber); 02153 } 02154 02155 void cChannelCamRelations::ClrDecrypt(tChannelID ChannelID, int CamSlotNumber) 02156 { 02157 cMutexLock MutexLock(&mutex); 02158 cChannelCamRelation *ccr = GetEntry(ChannelID); 02159 if (ccr) 02160 ccr->ClrDecrypt(CamSlotNumber); 02161 }