|
MythTV
0.26-pre
|
00001 /* 00002 * ci.cc: Common Interface 00003 * 00004 * Copyright (C) 2000 Klaus Schmidinger 00005 * 00006 * This program is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU General Public License 00008 * as published by the Free Software Foundation; either version 2 00009 * of the License, or (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 00019 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html 00020 * 00021 * The author can be reached at kls@cadsoft.de 00022 * 00023 * The project's page is at http://www.cadsoft.de/people/kls/vdr 00024 * 00025 */ 00026 00027 #include "dvbci.h" 00028 #include <errno.h> 00029 #include <ctype.h> 00030 #include <linux/dvb/ca.h> 00031 #include <malloc.h> 00032 #include <netinet/in.h> 00033 #include <poll.h> 00034 #include <string.h> 00035 #include <sys/ioctl.h> 00036 #include <sys/time.h> 00037 #include <time.h> 00038 #include <unistd.h> 00039 #include <fcntl.h> 00040 00041 #include <QString> 00042 00043 #include "mythlogging.h" 00044 00045 #ifndef MALLOC 00046 #define MALLOC(type, size) (type *)malloc(sizeof(type) * (size)) 00047 #endif 00048 00049 #define esyslog(a...) LOG(VB_GENERAL, LOG_ERR, QString().sprintf(a)) 00050 #define isyslog(a...) LOG(VB_DVBCAM, LOG_INFO, QString().sprintf(a)) 00051 #define dsyslog(a...) LOG(VB_DVBCAM, LOG_DEBUG, QString().sprintf(a)) 00052 00053 #define LOG_ERROR esyslog("ERROR (%s,%d): %m", __FILE__, __LINE__) 00054 #define LOG_ERROR_STR(s) esyslog("ERROR: %s: %m", s) 00055 00056 00057 // Set these to 'true' for debug output: 00058 static bool DumpTPDUDataTransfer = false; 00059 static bool DebugProtocol = false; 00060 static bool _connected = false; 00061 00062 #define dbgprotocol(a...) if (DebugProtocol) LOG(VB_DVBCAM, LOG_DEBUG, QString().sprintf(a)) 00063 00064 #define OK 0 00065 #define TIMEOUT -1 00066 #define ERROR -2 00067 00068 // --- Workarounds ----------------------------------------------------------- 00069 00070 // The Irdeto AllCAM 4.7 (and maybe others, too) does not react on AOT_ENTER_MENU 00071 // during the first few seconds of a newly established connection 00072 #define WRKRND_TIME_BEFORE_ENTER_MENU 15 // seconds 00073 00074 // --- Helper functions ------------------------------------------------------ 00075 00076 #define SIZE_INDICATOR 0x80 00077 00078 static ssize_t safe_read(int filedes, void *buffer, size_t size) 00079 { 00080 for (;;) { 00081 ssize_t p = read(filedes, buffer, size); 00082 if (p < 0 && (errno == EINTR || errno == EAGAIN)) { 00083 dsyslog("EINTR while reading from file handle %d - retrying", filedes); 00084 continue; 00085 } 00086 return p; 00087 } 00088 } 00089 00090 static const uint8_t *GetLength(const uint8_t *Data, int &Length) 00094 { 00095 Length = *Data++; 00096 if ((Length & SIZE_INDICATOR) != 0) { 00097 int l = Length & ~SIZE_INDICATOR; 00098 Length = 0; 00099 for (int i = 0; i < l; i++) 00100 Length = (Length << 8) | *Data++; 00101 } 00102 return Data; 00103 } 00104 00105 static uint8_t *SetLength(uint8_t *Data, int Length) 00108 { 00109 uint8_t *p = Data; 00110 if (Length < 128) 00111 *p++ = Length; 00112 else { 00113 int n = sizeof(Length); 00114 for (int i = n - 1; i >= 0; i--) { 00115 int b = (Length >> (8 * i)) & 0xFF; 00116 if (p != Data || b) 00117 *++p = b; 00118 } 00119 *Data = (p - Data) | SIZE_INDICATOR; 00120 p++; 00121 } 00122 return p; 00123 } 00124 00125 static char *CopyString(int Length, const uint8_t *Data) 00128 { 00129 char *s = MALLOC(char, Length + 1); 00130 strncpy(s, (char *)Data, Length); 00131 s[Length] = 0; 00132 return s; 00133 } 00134 00135 static char *GetString(int &Length, const uint8_t **Data) 00139 { 00140 if (Length > 0 && Data && *Data) { 00141 int l = 0; 00142 const uint8_t *d = GetLength(*Data, l); 00143 char *s = CopyString(l, d); 00144 Length -= d - *Data + l; 00145 *Data = d + l; 00146 return s; 00147 } 00148 return NULL; 00149 } 00150 00151 00152 00153 // --- cMutex ---------------------------------------------------------------- 00154 00155 cMutex::cMutex(void) 00156 { 00157 lockingPid = 0; 00158 locked = 0; 00159 pthread_mutex_init(&mutex, NULL); 00160 } 00161 00162 cMutex::~cMutex() 00163 { 00164 pthread_mutex_destroy(&mutex); 00165 } 00166 00167 void cMutex::Lock(void) 00168 { 00169 if (getpid() != lockingPid || !locked) { 00170 pthread_mutex_lock(&mutex); 00171 lockingPid = getpid(); 00172 } 00173 locked++; 00174 } 00175 00176 void cMutex::Unlock(void) 00177 { 00178 if (--locked <= 0) { 00179 if (locked < 0) { 00180 esyslog("cMutex Lock inbalance detected"); 00181 locked = 0; 00182 } 00183 lockingPid = 0; 00184 pthread_mutex_unlock(&mutex); 00185 } 00186 } 00187 // --- cMutexLock ------------------------------------------------------------ 00188 00189 cMutexLock::cMutexLock(cMutex *Mutex) 00190 { 00191 mutex = NULL; 00192 locked = false; 00193 Lock(Mutex); 00194 } 00195 00196 cMutexLock::~cMutexLock() 00197 { 00198 if (mutex && locked) 00199 mutex->Unlock(); 00200 } 00201 00202 bool cMutexLock::Lock(cMutex *Mutex) 00203 { 00204 if (Mutex && !mutex) { 00205 mutex = Mutex; 00206 Mutex->Lock(); 00207 locked = true; 00208 return true; 00209 } 00210 return false; 00211 } 00212 00213 00214 00215 // --- cTPDU ----------------------------------------------------------------- 00216 00217 #define MAX_TPDU_SIZE 2048 00218 #define MAX_TPDU_DATA (MAX_TPDU_SIZE - 4) 00219 00220 #define DATA_INDICATOR 0x80 00221 00222 #define T_SB 0x80 00223 #define T_RCV 0x81 00224 #define T_CREATE_TC 0x82 00225 #define T_CTC_REPLY 0x83 00226 #define T_DELETE_TC 0x84 00227 #define T_DTC_REPLY 0x85 00228 #define T_REQUEST_TC 0x86 00229 #define T_NEW_TC 0x87 00230 #define T_TC_ERROR 0x88 00231 #define T_DATA_LAST 0xA0 00232 #define T_DATA_MORE 0xA1 00233 00234 class cTPDU { 00235 private: 00236 int size; 00237 uint8_t data[MAX_TPDU_SIZE]; 00238 const uint8_t *GetData(const uint8_t *Data, int &Length); 00239 public: 00240 cTPDU(void) { size = 0; memset(data, 0, sizeof(uint8_t) * MAX_TPDU_SIZE); } 00241 cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length = 0, const uint8_t *Data = NULL); 00242 uint8_t Slot(void) { return data[0]; } 00243 uint8_t Tcid(void) { return data[1]; } 00244 uint8_t Tag(void) { return data[2]; } 00245 const uint8_t *Data(int &Length) { return GetData(data + 3, Length); } 00246 uint8_t Status(void); 00247 int Write(int fd); 00248 int Read(int fd); 00249 void Dump(bool Outgoing); 00250 }; 00251 00252 cTPDU::cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length, const uint8_t *Data) 00253 { 00254 size = 0; 00255 data[0] = Slot; 00256 data[1] = Tcid; 00257 data[2] = Tag; 00258 switch (Tag) { 00259 case T_RCV: 00260 case T_CREATE_TC: 00261 case T_CTC_REPLY: 00262 case T_DELETE_TC: 00263 case T_DTC_REPLY: 00264 case T_REQUEST_TC: 00265 data[3] = 1; // length 00266 data[4] = Tcid; 00267 size = 5; 00268 break; 00269 case T_NEW_TC: 00270 case T_TC_ERROR: 00271 if (Length == 1) { 00272 data[3] = 2; // length 00273 data[4] = Tcid; 00274 data[5] = Data[0]; 00275 size = 6; 00276 } 00277 else 00278 esyslog("ERROR: illegal data length for TPDU tag 0x%02X: %d", Tag, Length); 00279 break; 00280 case T_DATA_LAST: 00281 case T_DATA_MORE: 00282 if (Length <= MAX_TPDU_DATA) { 00283 uint8_t *p = data + 3; 00284 p = SetLength(p, Length + 1); 00285 *p++ = Tcid; 00286 if (Length) 00287 memcpy(p, Data, Length); 00288 size = Length + (p - data); 00289 } 00290 else 00291 esyslog("ERROR: illegal data length for TPDU tag 0x%02X: %d", Tag, Length); 00292 break; 00293 default: 00294 esyslog("ERROR: unknown TPDU tag: 0x%02X", Tag); 00295 } 00296 } 00297 00298 int cTPDU::Write(int fd) 00299 { 00300 Dump(true); 00301 if (size) 00302 return write(fd, data, size) == size ? OK : ERROR; 00303 esyslog("ERROR: attemp to write TPDU with zero size"); 00304 return ERROR; 00305 } 00306 00307 int cTPDU::Read(int fd) 00308 { 00309 size = safe_read(fd, data, sizeof(data)); 00310 if (size < 0) { 00311 esyslog("ERROR: %m"); 00312 size = 0; 00313 return ERROR; 00314 } 00315 Dump(false); 00316 return OK; 00317 } 00318 00319 void cTPDU::Dump(bool Outgoing) 00320 { 00321 if (DumpTPDUDataTransfer) { 00322 #define MAX_DUMP 256 00323 QString msg = QString("%1 ").arg(Outgoing ? "-->" : "<--"); 00324 for (int i = 0; i < size && i < MAX_DUMP; i++) 00325 msg += QString("%1 ").arg((short int)data[i], 2, 16, QChar('0')); 00326 if (size >= MAX_DUMP) 00327 msg += "..."; 00328 LOG(VB_DVBCAM, LOG_INFO, msg); 00329 if (!Outgoing) { 00330 msg = QString(" "); 00331 for (int i = 0; i < size && i < MAX_DUMP; i++) 00332 msg += QString("%1 ").arg(isprint(data[i]) ? data[i] : '.', 2); 00333 if (size >= MAX_DUMP) 00334 msg += "..."; 00335 LOG(VB_DVBCAM, LOG_INFO, msg); 00336 } 00337 } 00338 } 00339 00340 const uint8_t *cTPDU::GetData(const uint8_t *Data, int &Length) 00341 { 00342 if (size) { 00343 Data = GetLength(Data, Length); 00344 if (Length) { 00345 Length--; // the first byte is always the tcid 00346 return Data + 1; 00347 } 00348 } 00349 return NULL; 00350 } 00351 00352 uint8_t cTPDU::Status(void) 00353 { 00354 if (size >= 4 && data[size - 4] == T_SB && data[size - 3] == 2) { 00355 //XXX test tcid??? 00356 return data[size - 1]; 00357 } 00358 return 0; 00359 } 00360 00361 // --- cCiTransportConnection ------------------------------------------------ 00362 00363 enum eState { stIDLE, stCREATION, stACTIVE, stDELETION }; 00364 00365 class cCiTransportConnection { 00366 friend class cCiTransportLayer; 00367 private: 00368 int fd; 00369 uint8_t slot; 00370 uint8_t tcid; 00371 eState state; 00372 cTPDU *tpdu; 00373 struct timeval last_poll; 00374 int lastResponse; 00375 bool dataAvailable; 00376 void Init(int Fd, uint8_t Slot, uint8_t Tcid); 00377 int SendTPDU(uint8_t Tag, int Length = 0, const uint8_t *Data = NULL); 00378 int RecvTPDU(void); 00379 int CreateConnection(void); 00380 int Poll(void); 00381 eState State(void) { return state; } 00382 int LastResponse(void) { return lastResponse; } 00383 bool DataAvailable(void) { return dataAvailable; } 00384 public: 00385 cCiTransportConnection(void); 00386 ~cCiTransportConnection(); 00387 int Slot(void) const { return slot; } 00388 int SendData(int Length, const uint8_t *Data); 00389 int RecvData(void); 00390 const uint8_t *Data(int &Length); 00391 //XXX Close() 00392 }; 00393 00394 cCiTransportConnection::cCiTransportConnection(void) 00395 { 00396 tpdu = NULL; 00397 last_poll.tv_sec = 0; 00398 last_poll.tv_usec = 0; 00399 Init(-1, 0, 0); 00400 } 00401 00402 cCiTransportConnection::~cCiTransportConnection() 00403 { 00404 delete tpdu; 00405 } 00406 00407 void cCiTransportConnection::Init(int Fd, uint8_t Slot, uint8_t Tcid) 00408 { 00409 fd = Fd; 00410 slot = Slot; 00411 tcid = Tcid; 00412 state = stIDLE; 00413 if (fd >= 0 && !tpdu) 00414 tpdu = new cTPDU; 00415 lastResponse = ERROR; 00416 dataAvailable = false; 00417 //XXX Clear()??? 00418 } 00419 00420 int cCiTransportConnection::SendTPDU(uint8_t Tag, int Length, const uint8_t *Data) 00421 { 00422 cTPDU TPDU(slot, tcid, Tag, Length, Data); 00423 return TPDU.Write(fd); 00424 } 00425 00426 #define CAM_READ_TIMEOUT 5000 // ms 00427 00428 int cCiTransportConnection::RecvTPDU(void) 00429 { 00430 struct pollfd pfd[1]; 00431 pfd[0].fd = fd; 00432 pfd[0].events = POLLIN; 00433 lastResponse = ERROR; 00434 00435 for (;;) { 00436 int ret = poll(pfd, 1, CAM_READ_TIMEOUT); 00437 if (ret == -1 && (errno == EAGAIN || errno == EINTR)) 00438 continue; 00439 break; 00440 } 00441 00442 if ( 00443 (pfd[0].revents & POLLIN) && 00444 tpdu->Read(fd) == OK && 00445 tpdu->Tcid() == tcid 00446 ) 00447 { 00448 switch (state) { 00449 case stIDLE: break; 00450 case stCREATION: if (tpdu->Tag() == T_CTC_REPLY) { 00451 dataAvailable = tpdu->Status() & DATA_INDICATOR; 00452 state = stACTIVE; 00453 lastResponse = tpdu->Tag(); 00454 } 00455 break; 00456 case stACTIVE: switch (tpdu->Tag()) { 00457 case T_SB: 00458 case T_DATA_LAST: 00459 case T_DATA_MORE: 00460 case T_REQUEST_TC: break; 00461 case T_DELETE_TC: if (SendTPDU(T_DTC_REPLY) != OK) 00462 return ERROR; 00463 Init(fd, slot, tcid); 00464 break; 00465 default: return ERROR; 00466 } 00467 dataAvailable = tpdu->Status() & DATA_INDICATOR; 00468 lastResponse = tpdu->Tag(); 00469 break; 00470 case stDELETION: if (tpdu->Tag() == T_DTC_REPLY) { 00471 Init(fd, slot, tcid); 00472 //XXX Status()??? 00473 lastResponse = tpdu->Tag(); 00474 } 00475 break; 00476 } 00477 } 00478 else { 00479 esyslog("ERROR: CAM: Read failed: slot %d, tcid %d\n", slot, tcid); 00480 Init(-1, slot, tcid); 00481 } 00482 return lastResponse; 00483 } 00484 00485 int cCiTransportConnection::SendData(int Length, const uint8_t *Data) 00486 { 00487 while (state == stACTIVE && Length > 0) { 00488 uint8_t Tag = T_DATA_LAST; 00489 int l = Length; 00490 if (l > MAX_TPDU_DATA) { 00491 Tag = T_DATA_MORE; 00492 l = MAX_TPDU_DATA; 00493 } 00494 if (SendTPDU(Tag, l, Data) != OK || RecvTPDU() != T_SB) 00495 break; 00496 Length -= l; 00497 Data += l; 00498 } 00499 return Length ? ERROR : OK; 00500 } 00501 00502 int cCiTransportConnection::RecvData(void) 00503 { 00504 if (SendTPDU(T_RCV) == OK) 00505 return RecvTPDU(); 00506 return ERROR; 00507 } 00508 00509 const uint8_t *cCiTransportConnection::Data(int &Length) 00510 { 00511 return tpdu->Data(Length); 00512 } 00513 00514 #define MAX_CONNECT_RETRIES 25 00515 00516 int cCiTransportConnection::CreateConnection(void) 00517 { 00518 if (state == stIDLE) { 00519 if (SendTPDU(T_CREATE_TC) == OK) { 00520 state = stCREATION; 00521 if (RecvTPDU() == T_CTC_REPLY) { 00522 _connected=true; 00523 return OK; 00524 // the following is a workaround for CAMs that don't quite follow the specs... 00525 } else { 00526 for (int i = 0; i < MAX_CONNECT_RETRIES; i++) { 00527 dsyslog("CAM: retrying to establish connection"); 00528 if (RecvTPDU() == T_CTC_REPLY) { 00529 dsyslog("CAM: connection established"); 00530 _connected=true; 00531 return OK; 00532 } 00533 } 00534 return ERROR; 00535 } 00536 } 00537 } 00538 return ERROR; 00539 } 00540 00541 // Polls can be done with a 100ms interval (EN50221 - A.4.1.12) 00542 #define POLL_INTERVAL 100 00543 00544 int cCiTransportConnection::Poll(void) 00545 { 00546 struct timeval curr_time; 00547 00548 if (state != stACTIVE) 00549 return ERROR; 00550 00551 gettimeofday(&curr_time, 0); 00552 uint64_t msdiff = (curr_time.tv_sec * 1000) + (curr_time.tv_usec / 1000) - 00553 (last_poll.tv_sec * 1000) - (last_poll.tv_usec / 1000); 00554 00555 if (msdiff < POLL_INTERVAL) 00556 return OK; 00557 00558 last_poll.tv_sec = curr_time.tv_sec; 00559 last_poll.tv_usec = curr_time.tv_usec; 00560 00561 if (SendTPDU(T_DATA_LAST) != OK) 00562 return ERROR; 00563 00564 return RecvTPDU(); 00565 } 00566 00567 // --- cCiTransportLayer ----------------------------------------------------- 00568 00569 #define MAX_CI_CONNECT 16 // maximum possible value is 254 00570 00571 class cCiTransportLayer { 00572 private: 00573 int fd; 00574 int numSlots; 00575 cCiTransportConnection tc[MAX_CI_CONNECT]; 00576 public: 00577 cCiTransportLayer(int Fd, int NumSlots); 00578 cCiTransportConnection *NewConnection(int Slot); 00579 bool ResetSlot(int Slot); 00580 bool ModuleReady(int Slot); 00581 cCiTransportConnection *Process(int Slot); 00582 }; 00583 00584 cCiTransportLayer::cCiTransportLayer(int Fd, int NumSlots) 00585 { 00586 fd = Fd; 00587 numSlots = NumSlots; 00588 for (int s = 0; s < numSlots; s++) 00589 ResetSlot(s); 00590 } 00591 00592 cCiTransportConnection *cCiTransportLayer::NewConnection(int Slot) 00593 { 00594 for (int i = 0; i < MAX_CI_CONNECT; i++) { 00595 if (tc[i].State() == stIDLE) { 00596 dbgprotocol("Creating connection: slot %d, tcid %d\n", Slot, i + 1); 00597 tc[i].Init(fd, Slot, i + 1); 00598 if (tc[i].CreateConnection() == OK) 00599 return &tc[i]; 00600 break; 00601 } 00602 } 00603 return NULL; 00604 } 00605 00606 bool cCiTransportLayer::ResetSlot(int Slot) 00607 { 00608 dbgprotocol("Resetting slot %d...", Slot); 00609 if (ioctl(fd, CA_RESET, 1 << Slot) != -1) { 00610 dbgprotocol("ok.\n"); 00611 return true; 00612 } 00613 else 00614 esyslog("ERROR: can't reset CAM slot %d: %m", Slot); 00615 dbgprotocol("failed!\n"); 00616 return false; 00617 } 00618 00619 bool cCiTransportLayer::ModuleReady(int Slot) 00620 { 00621 ca_slot_info_t sinfo; 00622 sinfo.num = Slot; 00623 if (ioctl(fd, CA_GET_SLOT_INFO, &sinfo) != -1) 00624 return sinfo.flags & CA_CI_MODULE_READY; 00625 else 00626 esyslog("ERROR: can't get info on CAM slot %d: %m", Slot); 00627 return false; 00628 } 00629 00630 cCiTransportConnection *cCiTransportLayer::Process(int Slot) 00631 { 00632 for (int i = 0; i < MAX_CI_CONNECT; i++) { 00633 cCiTransportConnection *Tc = &tc[i]; 00634 if (Tc->Slot() == Slot) { 00635 switch (Tc->State()) { 00636 case stCREATION: 00637 case stACTIVE: 00638 if (!Tc->DataAvailable()) { 00639 if (Tc->Poll() != OK) 00640 ;//XXX continue; 00641 } 00642 switch (Tc->LastResponse()) { 00643 case T_REQUEST_TC: 00644 //XXX 00645 break; 00646 case T_DATA_MORE: 00647 case T_DATA_LAST: 00648 case T_CTC_REPLY: 00649 case T_SB: 00650 if (Tc->DataAvailable()) 00651 Tc->RecvData(); 00652 break; 00653 case TIMEOUT: 00654 case ERROR: 00655 default: 00656 //XXX Tc->state = stIDLE;//XXX Init()??? 00657 return NULL; 00658 break; 00659 } 00660 //XXX this will only work with _one_ transport connection per slot! 00661 return Tc; 00662 break; 00663 default: ; 00664 } 00665 } 00666 } 00667 return NULL; 00668 } 00669 00670 // -- cCiSession ------------------------------------------------------------- 00671 00672 // Session Tags: 00673 00674 #define ST_SESSION_NUMBER 0x90 00675 #define ST_OPEN_SESSION_REQUEST 0x91 00676 #define ST_OPEN_SESSION_RESPONSE 0x92 00677 #define ST_CREATE_SESSION 0x93 00678 #define ST_CREATE_SESSION_RESPONSE 0x94 00679 #define ST_CLOSE_SESSION_REQUEST 0x95 00680 #define ST_CLOSE_SESSION_RESPONSE 0x96 00681 00682 // Session Status: 00683 00684 #define SS_OK 0x00 00685 #define SS_NOT_ALLOCATED 0xF0 00686 00687 // Resource Identifiers: 00688 00689 #define RI_RESOURCE_MANAGER 0x00010041 00690 #define RI_APPLICATION_INFORMATION 0x00020041 00691 #define RI_CONDITIONAL_ACCESS_SUPPORT 0x00030041 00692 #define RI_HOST_CONTROL 0x00200041 00693 #define RI_DATE_TIME 0x00240041 00694 #define RI_MMI 0x00400041 00695 00696 // Application Object Tags: 00697 00698 #define AOT_NONE 0x000000 00699 #define AOT_PROFILE_ENQ 0x9F8010 00700 #define AOT_PROFILE 0x9F8011 00701 #define AOT_PROFILE_CHANGE 0x9F8012 00702 #define AOT_APPLICATION_INFO_ENQ 0x9F8020 00703 #define AOT_APPLICATION_INFO 0x9F8021 00704 #define AOT_ENTER_MENU 0x9F8022 00705 #define AOT_CA_INFO_ENQ 0x9F8030 00706 #define AOT_CA_INFO 0x9F8031 00707 #define AOT_CA_PMT 0x9F8032 00708 #define AOT_CA_PMT_REPLY 0x9F8033 00709 #define AOT_TUNE 0x9F8400 00710 #define AOT_REPLACE 0x9F8401 00711 #define AOT_CLEAR_REPLACE 0x9F8402 00712 #define AOT_ASK_RELEASE 0x9F8403 00713 #define AOT_DATE_TIME_ENQ 0x9F8440 00714 #define AOT_DATE_TIME 0x9F8441 00715 #define AOT_CLOSE_MMI 0x9F8800 00716 #define AOT_DISPLAY_CONTROL 0x9F8801 00717 #define AOT_DISPLAY_REPLY 0x9F8802 00718 #define AOT_TEXT_LAST 0x9F8803 00719 #define AOT_TEXT_MORE 0x9F8804 00720 #define AOT_KEYPAD_CONTROL 0x9F8805 00721 #define AOT_KEYPRESS 0x9F8806 00722 #define AOT_ENQ 0x9F8807 00723 #define AOT_ANSW 0x9F8808 00724 #define AOT_MENU_LAST 0x9F8809 00725 #define AOT_MENU_MORE 0x9F880A 00726 #define AOT_MENU_ANSW 0x9F880B 00727 #define AOT_LIST_LAST 0x9F880C 00728 #define AOT_LIST_MORE 0x9F880D 00729 #define AOT_SUBTITLE_SEGMENT_LAST 0x9F880E 00730 #define AOT_SUBTITLE_SEGMENT_MORE 0x9F880F 00731 #define AOT_DISPLAY_MESSAGE 0x9F8810 00732 #define AOT_SCENE_END_MARK 0x9F8811 00733 #define AOT_SCENE_DONE 0x9F8812 00734 #define AOT_SCENE_CONTROL 0x9F8813 00735 #define AOT_SUBTITLE_DOWNLOAD_LAST 0x9F8814 00736 #define AOT_SUBTITLE_DOWNLOAD_MORE 0x9F8815 00737 #define AOT_FLUSH_DOWNLOAD 0x9F8816 00738 #define AOT_DOWNLOAD_REPLY 0x9F8817 00739 #define AOT_COMMS_CMD 0x9F8C00 00740 #define AOT_CONNECTION_DESCRIPTOR 0x9F8C01 00741 #define AOT_COMMS_REPLY 0x9F8C02 00742 #define AOT_COMMS_SEND_LAST 0x9F8C03 00743 #define AOT_COMMS_SEND_MORE 0x9F8C04 00744 #define AOT_COMMS_RCV_LAST 0x9F8C05 00745 #define AOT_COMMS_RCV_MORE 0x9F8C06 00746 00747 class cCiSession { 00748 private: 00749 int sessionId; 00750 int resourceId; 00751 cCiTransportConnection *tc; 00752 protected: 00753 int GetTag(int &Length, const uint8_t **Data); 00754 const uint8_t *GetData(const uint8_t *Data, int &Length); 00755 int SendData(int Tag, int Length = 0, const uint8_t *Data = NULL); 00756 public: 00757 cCiSession(int SessionId, int ResourceId, cCiTransportConnection *Tc); 00758 virtual ~cCiSession(); 00759 const cCiTransportConnection *Tc(void) { return tc; } 00760 int SessionId(void) { return sessionId; } 00761 int ResourceId(void) { return resourceId; } 00762 virtual bool HasUserIO(void) { return false; } 00763 virtual bool Process(int Length = 0, const uint8_t *Data = NULL); 00764 }; 00765 00766 cCiSession::cCiSession(int SessionId, int ResourceId, cCiTransportConnection *Tc) 00767 { 00768 sessionId = SessionId; 00769 resourceId = ResourceId; 00770 tc = Tc; 00771 } 00772 00773 cCiSession::~cCiSession() 00774 { 00775 } 00776 00777 int cCiSession::GetTag(int &Length, const uint8_t **Data) 00781 { 00782 if (Length >= 3 && Data && *Data) { 00783 int t = 0; 00784 for (int i = 0; i < 3; i++) 00785 t = (t << 8) | *(*Data)++; 00786 Length -= 3; 00787 return t; 00788 } 00789 return AOT_NONE; 00790 } 00791 00792 const uint8_t *cCiSession::GetData(const uint8_t *Data, int &Length) 00793 { 00794 Data = GetLength(Data, Length); 00795 return Length ? Data : NULL; 00796 } 00797 00798 int cCiSession::SendData(int Tag, int Length, const uint8_t *Data) 00799 { 00800 uint8_t buffer[2048]; 00801 uint8_t *p = buffer; 00802 *p++ = ST_SESSION_NUMBER; 00803 *p++ = 0x02; 00804 *p++ = (sessionId >> 8) & 0xFF; 00805 *p++ = sessionId & 0xFF; 00806 *p++ = (Tag >> 16) & 0xFF; 00807 *p++ = (Tag >> 8) & 0xFF; 00808 *p++ = Tag & 0xFF; 00809 p = SetLength(p, Length); 00810 if (p - buffer + Length < int(sizeof(buffer))) { 00811 memcpy(p, Data, Length); 00812 p += Length; 00813 return tc->SendData(p - buffer, buffer); 00814 } 00815 esyslog("ERROR: CAM: data length (%d) exceeds buffer size", Length); 00816 return ERROR; 00817 } 00818 00819 bool cCiSession::Process(int Length, const uint8_t *Data) 00820 { 00821 (void)Length; 00822 (void)Data; 00823 return true; 00824 } 00825 00826 // -- cCiResourceManager ----------------------------------------------------- 00827 00828 class cCiResourceManager : public cCiSession { 00829 private: 00830 int state; 00831 public: 00832 cCiResourceManager(int SessionId, cCiTransportConnection *Tc); 00833 virtual bool Process(int Length = 0, const uint8_t *Data = NULL); 00834 }; 00835 00836 cCiResourceManager::cCiResourceManager(int SessionId, cCiTransportConnection *Tc) 00837 :cCiSession(SessionId, RI_RESOURCE_MANAGER, Tc) 00838 { 00839 dbgprotocol("New Resource Manager (session id %d)\n", SessionId); 00840 state = 0; 00841 } 00842 00843 bool cCiResourceManager::Process(int Length, const uint8_t *Data) 00844 { 00845 if (Data) { 00846 int Tag = GetTag(Length, &Data); 00847 switch (Tag) { 00848 case AOT_PROFILE_ENQ: { 00849 dbgprotocol("%d: <== Profile Enquiry\n", SessionId()); 00850 int resources[] = { htonl(RI_RESOURCE_MANAGER), 00851 htonl(RI_APPLICATION_INFORMATION), 00852 htonl(RI_CONDITIONAL_ACCESS_SUPPORT), 00853 htonl(RI_DATE_TIME), 00854 htonl(RI_MMI) 00855 }; 00856 dbgprotocol("%d: ==> Profile\n", SessionId()); 00857 SendData(AOT_PROFILE, sizeof(resources), (uint8_t*)resources); 00858 state = 3; 00859 } 00860 break; 00861 case AOT_PROFILE: { 00862 dbgprotocol("%d: <== Profile\n", SessionId()); 00863 if (state == 1) { 00864 int l = 0; 00865 const uint8_t *d = GetData(Data, l); 00866 if (l > 0 && d) 00867 esyslog("CI resource manager: unexpected data"); 00868 dbgprotocol("%d: ==> Profile Change\n", SessionId()); 00869 SendData(AOT_PROFILE_CHANGE); 00870 state = 2; 00871 } 00872 else { 00873 esyslog("ERROR: CI resource manager: unexpected tag %06X in state %d", Tag, state); 00874 } 00875 } 00876 break; 00877 default: esyslog("ERROR: CI resource manager: unknown tag %06X", Tag); 00878 return false; 00879 } 00880 } 00881 else if (state == 0) { 00882 dbgprotocol("%d: ==> Profile Enq\n", SessionId()); 00883 SendData(AOT_PROFILE_ENQ); 00884 state = 1; 00885 } 00886 return true; 00887 } 00888 00889 // --- cCiApplicationInformation --------------------------------------------- 00890 00891 class cCiApplicationInformation : public cCiSession { 00892 private: 00893 int state; 00894 time_t creationTime; 00895 uint8_t applicationType; 00896 uint16_t applicationManufacturer; 00897 uint16_t manufacturerCode; 00898 char *menuString; 00899 public: 00900 cCiApplicationInformation(int SessionId, cCiTransportConnection *Tc); 00901 virtual ~cCiApplicationInformation(); 00902 virtual bool Process(int Length = 0, const uint8_t *Data = NULL); 00903 bool EnterMenu(void); 00904 char *GetApplicationString() { return strdup(menuString); }; 00905 uint16_t GetApplicationManufacturer() { return applicationManufacturer; }; 00906 uint16_t GetManufacturerCode() { return manufacturerCode; }; 00907 }; 00908 00909 cCiApplicationInformation::cCiApplicationInformation(int SessionId, cCiTransportConnection *Tc) 00910 :cCiSession(SessionId, RI_APPLICATION_INFORMATION, Tc) 00911 { 00912 dbgprotocol("New Application Information (session id %d)\n", SessionId); 00913 state = 0; 00914 creationTime = time(NULL); 00915 applicationType = 0; 00916 applicationManufacturer = 0; 00917 manufacturerCode = 0; 00918 menuString = NULL; 00919 } 00920 00921 cCiApplicationInformation::~cCiApplicationInformation() 00922 { 00923 free(menuString); 00924 } 00925 00926 bool cCiApplicationInformation::Process(int Length, const uint8_t *Data) 00927 { 00928 if (Data) { 00929 int Tag = GetTag(Length, &Data); 00930 switch (Tag) { 00931 case AOT_APPLICATION_INFO: { 00932 dbgprotocol("%d: <== Application Info\n", SessionId()); 00933 int l = 0; 00934 const uint8_t *d = GetData(Data, l); 00935 if ((l -= 1) < 0) break; 00936 applicationType = *d++; 00937 if ((l -= 2) < 0) break; 00938 applicationManufacturer = ntohs(*(uint16_t *)d); 00939 d += 2; 00940 if ((l -= 2) < 0) break; 00941 manufacturerCode = ntohs(*(uint16_t *)d); 00942 d += 2; 00943 free(menuString); 00944 menuString = GetString(l, &d); 00945 isyslog("CAM: %s, %02X, %04X, %04X", menuString, applicationType, 00946 applicationManufacturer, manufacturerCode); 00947 } 00948 state = 2; 00949 break; 00950 default: esyslog("ERROR: CI application information: unknown tag %06X", Tag); 00951 return false; 00952 } 00953 } 00954 else if (state == 0) { 00955 dbgprotocol("%d: ==> Application Info Enq\n", SessionId()); 00956 SendData(AOT_APPLICATION_INFO_ENQ); 00957 state = 1; 00958 } 00959 return true; 00960 } 00961 00962 bool cCiApplicationInformation::EnterMenu(void) 00963 { 00964 if (state == 2 && time(NULL) - creationTime > WRKRND_TIME_BEFORE_ENTER_MENU) { 00965 dbgprotocol("%d: ==> Enter Menu\n", SessionId()); 00966 SendData(AOT_ENTER_MENU); 00967 return true;//XXX 00968 } 00969 return false; 00970 } 00971 00972 // --- cCiConditionalAccessSupport ------------------------------------------- 00973 00974 class cCiConditionalAccessSupport : public cCiSession { 00975 private: 00976 int state; 00977 int numCaSystemIds; 00978 unsigned short caSystemIds[MAXCASYSTEMIDS + 1]; // list is zero terminated! 00979 bool needCaPmt; 00980 public: 00981 cCiConditionalAccessSupport(int SessionId, cCiTransportConnection *Tc); 00982 virtual bool Process(int Length = 0, const uint8_t *Data = NULL); 00983 const unsigned short *GetCaSystemIds(void) { return caSystemIds; } 00984 bool SendPMT(cCiCaPmt &CaPmt); 00985 bool NeedCaPmt(void) { return needCaPmt; } 00986 }; 00987 00988 cCiConditionalAccessSupport::cCiConditionalAccessSupport( 00989 int SessionId, cCiTransportConnection *Tc) : 00990 cCiSession(SessionId, RI_CONDITIONAL_ACCESS_SUPPORT, Tc), 00991 state(0), numCaSystemIds(0), needCaPmt(false) 00992 { 00993 dbgprotocol("New Conditional Access Support (session id %d)\n", SessionId); 00994 memset(caSystemIds, 0, sizeof(caSystemIds)); 00995 } 00996 00997 bool cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data) 00998 { 00999 if (Data) { 01000 int Tag = GetTag(Length, &Data); 01001 switch (Tag) { 01002 case AOT_CA_INFO: { 01003 dbgprotocol("%d: <== Ca Info", SessionId()); 01004 int l = 0; 01005 const uint8_t *d = GetData(Data, l); 01006 while (l > 1) { 01007 unsigned short id = ((unsigned short)(*d) << 8) | *(d + 1); 01008 dbgprotocol(" %04X", id); 01009 d += 2; 01010 l -= 2; 01011 if (numCaSystemIds < MAXCASYSTEMIDS) { 01012 int i = 0; 01013 // Make sure the id is not already present 01014 for (; i < numCaSystemIds; i++) 01015 if (caSystemIds[i] == id) 01016 break; 01017 01018 if (i < numCaSystemIds) 01019 continue; 01020 01021 caSystemIds[numCaSystemIds++] = id; 01022 caSystemIds[numCaSystemIds] = 0; 01023 } 01024 else 01025 esyslog("ERROR: too many CA system IDs!"); 01026 } 01027 dbgprotocol("\n"); 01028 } 01029 state = 2; 01030 needCaPmt = true; 01031 break; 01032 default: esyslog("ERROR: CI conditional access support: unknown tag %06X", Tag); 01033 return false; 01034 } 01035 } 01036 else if (state == 0) { 01037 dbgprotocol("%d: ==> Ca Info Enq\n", SessionId()); 01038 SendData(AOT_CA_INFO_ENQ); 01039 state = 1; 01040 } 01041 return true; 01042 } 01043 01044 bool cCiConditionalAccessSupport::SendPMT(cCiCaPmt &CaPmt) 01045 { 01046 if (state == 2) { 01047 SendData(AOT_CA_PMT, CaPmt.length, CaPmt.capmt); 01048 needCaPmt = false; 01049 return true; 01050 } 01051 return false; 01052 } 01053 01054 // --- cCiDateTime ----------------------------------------------------------- 01055 01056 class cCiDateTime : public cCiSession { 01057 private: 01058 int interval; 01059 time_t lastTime; 01060 int timeOffset; 01061 bool SendDateTime(void); 01062 public: 01063 cCiDateTime(int SessionId, cCiTransportConnection *Tc); 01064 virtual bool Process(int Length = 0, const uint8_t *Data = NULL); 01065 void SetTimeOffset(double offset); 01066 }; 01067 01068 cCiDateTime::cCiDateTime(int SessionId, cCiTransportConnection *Tc) 01069 :cCiSession(SessionId, RI_DATE_TIME, Tc) 01070 { 01071 interval = 0; 01072 lastTime = 0; 01073 timeOffset = 0; 01074 dbgprotocol("New Date Time (session id %d)\n", SessionId); 01075 } 01076 01077 void cCiDateTime::SetTimeOffset(double offset) 01078 { 01079 timeOffset = (int) offset; 01080 dbgprotocol("New Time Offset: %i secs\n", timeOffset); 01081 } 01082 01083 bool cCiDateTime::SendDateTime(void) 01084 { 01085 time_t t = time(NULL); 01086 struct tm tm_gmt; 01087 struct tm tm_loc; 01088 01089 // Avoid using signed time_t types 01090 if (timeOffset < 0) 01091 t -= (time_t)(-timeOffset); 01092 else 01093 t += (time_t)(timeOffset); 01094 01095 if (gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc)) { 01096 int Y = tm_gmt.tm_year; 01097 int M = tm_gmt.tm_mon + 1; 01098 int D = tm_gmt.tm_mday; 01099 int L = (M == 1 || M == 2) ? 1 : 0; 01100 int MJD = 14956 + D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001); 01101 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10)) 01102 struct tTime { unsigned short mjd; uint8_t h, m, s; short offset; }; 01103 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) }; 01104 dbgprotocol("%d: ==> Date Time\n", SessionId()); 01105 SendData(AOT_DATE_TIME, 7, (uint8_t*)&T); 01106 //XXX return value of all SendData() calls??? 01107 return true; 01108 } 01109 return false; 01110 } 01111 01112 bool cCiDateTime::Process(int Length, const uint8_t *Data) 01113 { 01114 if (Data) { 01115 int Tag = GetTag(Length, &Data); 01116 switch (Tag) { 01117 case AOT_DATE_TIME_ENQ: { 01118 interval = 0; 01119 int l = 0; 01120 const uint8_t *d = GetData(Data, l); 01121 if (l > 0) 01122 interval = *d; 01123 dbgprotocol("%d: <== Date Time Enq, interval = %d\n", SessionId(), interval); 01124 lastTime = time(NULL); 01125 return SendDateTime(); 01126 } 01127 break; 01128 default: esyslog("ERROR: CI date time: unknown tag %06X", Tag); 01129 return false; 01130 } 01131 } 01132 else if (interval && time(NULL) - lastTime > interval) { 01133 lastTime = time(NULL); 01134 return SendDateTime(); 01135 } 01136 return true; 01137 } 01138 01139 // --- cCiMMI ---------------------------------------------------------------- 01140 01141 // Close MMI Commands: 01142 01143 #define CLOSE_MMI_IMMEDIATE 0x00 01144 #define CLOSE_MMI_DELAY 0x01 01145 01146 // Display Control Commands: 01147 01148 #define DCC_SET_MMI_MODE 0x01 01149 #define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02 01150 #define DCC_INPUT_CHARACTER_TABLE_LIST 0x03 01151 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04 01152 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05 01153 01154 // MMI Modes: 01155 01156 #define MM_HIGH_LEVEL 0x01 01157 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02 01158 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03 01159 01160 // Display Reply IDs: 01161 01162 #define DRI_MMI_MODE_ACK 0x01 01163 #define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02 01164 #define DRI_LIST_INPUT_CHARACTER_TABLES 0x03 01165 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04 01166 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05 01167 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0 01168 #define DRI_UNKNOWN_MMI_MODE 0xF1 01169 #define DRI_UNKNOWN_CHARACTER_TABLE 0xF2 01170 01171 // Enquiry Flags: 01172 01173 #define EF_BLIND 0x01 01174 01175 // Answer IDs: 01176 01177 #define AI_CANCEL 0x00 01178 #define AI_ANSWER 0x01 01179 01180 class cCiMMI : public cCiSession { 01181 private: 01182 char *GetText(int &Length, const uint8_t **Data); 01183 cCiMenu *menu; 01184 cCiEnquiry *enquiry; 01185 public: 01186 cCiMMI(int SessionId, cCiTransportConnection *Tc); 01187 virtual ~cCiMMI(); 01188 virtual bool Process(int Length = 0, const uint8_t *Data = NULL); 01189 virtual bool HasUserIO(void) { return menu || enquiry; } 01190 cCiMenu *Menu(void); 01191 cCiEnquiry *Enquiry(void); 01192 bool SendMenuAnswer(uint8_t Selection); 01193 bool SendAnswer(const char *Text); 01194 }; 01195 01196 cCiMMI::cCiMMI(int SessionId, cCiTransportConnection *Tc) 01197 :cCiSession(SessionId, RI_MMI, Tc) 01198 { 01199 dbgprotocol("New MMI (session id %d)\n", SessionId); 01200 menu = NULL; 01201 enquiry = NULL; 01202 } 01203 01204 cCiMMI::~cCiMMI() 01205 { 01206 delete menu; 01207 delete enquiry; 01208 } 01209 01210 char *cCiMMI::GetText(int &Length, const uint8_t **Data) 01214 { 01215 int Tag = GetTag(Length, Data); 01216 if (Tag == AOT_TEXT_LAST) { 01217 char *s = GetString(Length, Data); 01218 dbgprotocol("%d: <== Text Last '%s'\n", SessionId(), s); 01219 return s; 01220 } 01221 else 01222 esyslog("CI MMI: unexpected text tag: %06X", Tag); 01223 return NULL; 01224 } 01225 01226 bool cCiMMI::Process(int Length, const uint8_t *Data) 01227 { 01228 if (Data) { 01229 int Tag = GetTag(Length, &Data); 01230 switch (Tag) { 01231 case AOT_DISPLAY_CONTROL: { 01232 dbgprotocol("%d: <== Display Control\n", SessionId()); 01233 int l = 0; 01234 const uint8_t *d = GetData(Data, l); 01235 if (l > 0) { 01236 switch (*d) { 01237 case DCC_SET_MMI_MODE: 01238 if (l == 2 && *++d == MM_HIGH_LEVEL) { 01239 struct tDisplayReply { uint8_t id; uint8_t mode; }; 01240 tDisplayReply dr = { id : DRI_MMI_MODE_ACK, mode : MM_HIGH_LEVEL }; 01241 dbgprotocol("%d: ==> Display Reply\n", SessionId()); 01242 SendData(AOT_DISPLAY_REPLY, 2, (uint8_t *)&dr); 01243 } 01244 break; 01245 default: esyslog("CI MMI: unsupported display control command %02X", *d); 01246 return false; 01247 } 01248 } 01249 } 01250 break; 01251 case AOT_LIST_LAST: 01252 case AOT_MENU_LAST: { 01253 dbgprotocol("%d: <== Menu Last\n", SessionId()); 01254 delete menu; 01255 menu = new cCiMenu(this, Tag == AOT_MENU_LAST); 01256 int l = 0; 01257 const uint8_t *d = GetData(Data, l); 01258 if (l > 0) { 01259 // since the specification allows choiceNb to be undefined it is useless, so let's just skip it: 01260 d++; 01261 l--; 01262 if (l > 0) menu->titleText = GetText(l, &d); 01263 if (l > 0) menu->subTitleText = GetText(l, &d); 01264 if (l > 0) menu->bottomText = GetText(l, &d); 01265 while (l > 0) { 01266 char *s = GetText(l, &d); 01267 if (s) { 01268 if (!menu->AddEntry(s)) 01269 free(s); 01270 } 01271 else 01272 break; 01273 } 01274 } 01275 } 01276 break; 01277 case AOT_ENQ: { 01278 dbgprotocol("%d: <== Enq\n", SessionId()); 01279 delete enquiry; 01280 enquiry = new cCiEnquiry(this); 01281 int l = 0; 01282 const uint8_t *d = GetData(Data, l); 01283 if (l > 0) { 01284 uint8_t blind = *d++; 01285 //XXX GetByte()??? 01286 l--; 01287 enquiry->blind = blind & EF_BLIND; 01288 enquiry->expectedLength = *d++; 01289 l--; 01290 // I really wonder why there is no text length field here... 01291 enquiry->text = CopyString(l, d); 01292 } 01293 } 01294 break; 01295 case AOT_CLOSE_MMI: { 01296 int l = 0; 01297 const uint8_t *d = GetData(Data, l); 01298 01299 if(l > 0){ 01300 switch(*d){ 01301 case CLOSE_MMI_IMMEDIATE: 01302 dbgprotocol("%d <== Menu Close: immediate\n", SessionId()); 01303 break; 01304 case CLOSE_MMI_DELAY: 01305 dbgprotocol("%d <== Menu Close: delay\n", SessionId()); 01306 break; 01307 default: esyslog("ERROR: CI MMI: unknown close_mmi_cmd_id %02X", *d); 01308 return false; 01309 } 01310 } 01311 01312 break; 01313 } 01314 default: esyslog("ERROR: CI MMI: unknown tag %06X", Tag); 01315 return false; 01316 } 01317 } 01318 return true; 01319 } 01320 01321 cCiMenu *cCiMMI::Menu(void) 01322 { 01323 cCiMenu *m = menu; 01324 menu = NULL; 01325 return m; 01326 } 01327 01328 cCiEnquiry *cCiMMI::Enquiry(void) 01329 { 01330 cCiEnquiry *e = enquiry; 01331 enquiry = NULL; 01332 return e; 01333 } 01334 01335 bool cCiMMI::SendMenuAnswer(uint8_t Selection) 01336 { 01337 dbgprotocol("%d: ==> Menu Answ\n", SessionId()); 01338 SendData(AOT_MENU_ANSW, 1, &Selection); 01339 //XXX return value of all SendData() calls??? 01340 return true; 01341 } 01342 01343 bool cCiMMI::SendAnswer(const char *Text) 01344 { 01345 dbgprotocol("%d: ==> Answ\n", SessionId()); 01346 struct tAnswer { uint8_t id; char text[256]; };//XXX 01347 tAnswer answer; 01348 answer.id = Text ? AI_ANSWER : AI_CANCEL; 01349 if (Text) { 01350 strncpy(answer.text, Text, sizeof(answer.text) - 1); 01351 answer.text[255] = '\0'; 01352 } 01353 SendData(AOT_ANSW, Text ? strlen(Text) + 1 : 1, (uint8_t *)&answer); 01354 //XXX return value of all SendData() calls??? 01355 return true; 01356 } 01357 01358 // --- cCiMenu --------------------------------------------------------------- 01359 01360 cCiMenu::cCiMenu(cCiMMI *MMI, bool Selectable) 01361 { 01362 mmi = MMI; 01363 selectable = Selectable; 01364 titleText = subTitleText = bottomText = NULL; 01365 numEntries = 0; 01366 for (int i = 0; i < MAX_CIMENU_ENTRIES; i++) 01367 entries[i] = NULL; 01368 } 01369 01370 cCiMenu::~cCiMenu() 01371 { 01372 free(titleText); 01373 free(subTitleText); 01374 free(bottomText); 01375 for (int i = 0; i < numEntries; i++) 01376 free(entries[i]); 01377 } 01378 01379 bool cCiMenu::AddEntry(char *s) 01380 { 01381 if (numEntries < MAX_CIMENU_ENTRIES) { 01382 entries[numEntries++] = s; 01383 return true; 01384 } 01385 return false; 01386 } 01387 01388 bool cCiMenu::Select(int Index) 01389 { 01390 if (mmi && -1 <= Index && Index < numEntries) 01391 return mmi->SendMenuAnswer(Index + 1); 01392 return false; 01393 } 01394 01395 bool cCiMenu::Cancel(void) 01396 { 01397 return Select(-1); 01398 } 01399 01400 // --- cCiEnquiry ------------------------------------------------------------ 01401 01402 cCiEnquiry::cCiEnquiry(cCiMMI *MMI) 01403 { 01404 mmi = MMI; 01405 text = NULL; 01406 blind = false;; 01407 expectedLength = 0;; 01408 } 01409 01410 cCiEnquiry::~cCiEnquiry() 01411 { 01412 free(text); 01413 } 01414 01415 bool cCiEnquiry::Reply(const char *s) 01416 { 01417 return mmi ? mmi->SendAnswer(s) : false; 01418 } 01419 01420 bool cCiEnquiry::Cancel(void) 01421 { 01422 return Reply(NULL); 01423 } 01424 01425 // --- cCiCaPmt -------------------------------------------------------------- 01426 01427 // Ca Pmt Cmd Ids: 01428 01429 #define CPCI_OK_DESCRAMBLING 0x01 01430 #define CPCI_OK_MMI 0x02 01431 #define CPCI_QUERY 0x03 01432 #define CPCI_NOT_SELECTED 0x04 01433 01434 cCiCaPmt::cCiCaPmt(int ProgramNumber, uint8_t cplm) 01435 { 01436 length = 0; 01437 capmt[length++] = cplm; // ca_pmt_list_management 01438 capmt[length++] = (ProgramNumber >> 8) & 0xFF; 01439 capmt[length++] = ProgramNumber & 0xFF; 01440 capmt[length++] = 0x01; // version_number, current_next_indicator - apparently vn doesn't matter, but cni must be 1 01441 01442 // program_info_length 01443 infoLengthPos = length; 01444 capmt[length++] = 0x00; 01445 capmt[length++] = 0x00; 01446 } 01447 01448 void cCiCaPmt::AddElementaryStream(int type, int pid) 01449 { 01450 if (length + 5 > int(sizeof(capmt))) 01451 { 01452 esyslog("ERROR: buffer overflow in CA_PMT"); 01453 return; 01454 } 01455 01456 capmt[length++] = type & 0xFF; 01457 capmt[length++] = (pid >> 8) & 0xFF; 01458 capmt[length++] = pid & 0xFF; 01459 01460 // ES_info_length 01461 infoLengthPos = length; 01462 capmt[length++] = 0x00; 01463 capmt[length++] = 0x00; 01464 } 01465 01483 void cCiCaPmt::AddCaDescriptor(int ca_system_id, int ca_pid, int data_len, 01484 const uint8_t *data) 01485 { 01486 if (!infoLengthPos) 01487 { 01488 esyslog("ERROR: adding CA descriptor without program/stream!"); 01489 return; 01490 } 01491 01492 if (length + data_len + 7 > int(sizeof(capmt))) 01493 { 01494 esyslog("ERROR: buffer overflow in CA_PMT"); 01495 return; 01496 } 01497 01498 // We are either at start of program descriptors or stream descriptors. 01499 if (infoLengthPos + 2 == length) 01500 capmt[length++] = CPCI_OK_DESCRAMBLING; // ca_pmt_cmd_id 01501 01502 capmt[length++] = 0x09; // CA descriptor tag 01503 capmt[length++] = 4 + data_len; // descriptor length 01504 01505 capmt[length++] = (ca_system_id >> 8) & 0xFF; 01506 capmt[length++] = ca_system_id & 0xFF; 01507 capmt[length++] = (ca_pid >> 8) & 0xFF; 01508 capmt[length++] = ca_pid & 0xFF; 01509 01510 if (data_len > 0) 01511 { 01512 memcpy(&capmt[length], data, data_len); 01513 length += data_len; 01514 } 01515 01516 // update program_info_length/ES_info_length 01517 int l = length - infoLengthPos - 2; 01518 capmt[infoLengthPos] = (l >> 8) & 0xFF; 01519 capmt[infoLengthPos + 1] = l & 0xFF; 01520 } 01521 01522 // -- cLlCiHandler ------------------------------------------------------------- 01523 01524 cLlCiHandler::cLlCiHandler(int Fd, int NumSlots) 01525 { 01526 numSlots = NumSlots; 01527 newCaSupport = false; 01528 hasUserIO = false; 01529 for (int i = 0; i < MAX_CI_SESSION; i++) 01530 sessions[i] = NULL; 01531 tpl = new cCiTransportLayer(Fd, numSlots); 01532 tc = NULL; 01533 fdCa = Fd; 01534 needCaPmt = false; 01535 } 01536 01537 cLlCiHandler::~cLlCiHandler() 01538 { 01539 cMutexLock MutexLock(&mutex); 01540 for (int i = 0; i < MAX_CI_SESSION; i++) 01541 if (sessions[i] != NULL) 01542 delete sessions[i]; 01543 delete tpl; 01544 close(fdCa); 01545 } 01546 01547 cCiHandler *cCiHandler::CreateCiHandler(const char *FileName) 01548 { 01549 int fd_ca = open(FileName, O_RDWR); 01550 if (fd_ca >= 0) 01551 { 01552 ca_caps_t Caps; 01553 if (ioctl(fd_ca, CA_GET_CAP, &Caps) == 0) 01554 { 01555 int NumSlots = Caps.slot_num; 01556 if (NumSlots > 0) 01557 { 01558 if (Caps.slot_type & CA_CI_LINK) 01559 return new cLlCiHandler(fd_ca, NumSlots); 01560 else if (Caps.slot_type & CA_CI) 01561 return new cHlCiHandler(fd_ca, NumSlots); 01562 else 01563 isyslog("CAM doesn't support either high or low level CI," 01564 " Caps.slot_type=%i", Caps.slot_type); 01565 } 01566 else 01567 esyslog("ERROR: no CAM slots found"); 01568 } 01569 else 01570 LOG_ERROR_STR(FileName); 01571 close(fd_ca); 01572 } 01573 return NULL; 01574 } 01575 01576 int cLlCiHandler::ResourceIdToInt(const uint8_t *Data) 01577 { 01578 return (ntohl(*(int *)Data)); 01579 } 01580 01581 bool cLlCiHandler::Send(uint8_t Tag, int SessionId, int ResourceId, int Status) 01582 { 01583 uint8_t buffer[16]; 01584 uint8_t *p = buffer; 01585 *p++ = Tag; 01586 *p++ = 0x00; // will contain length 01587 if (Status >= 0) 01588 *p++ = Status; 01589 if (ResourceId) { 01590 *(int *)p = htonl(ResourceId); 01591 p += 4; 01592 } 01593 *(short *)p = htons(SessionId); 01594 p += 2; 01595 buffer[1] = p - buffer - 2; // length 01596 return tc && tc->SendData(p - buffer, buffer) == OK; 01597 } 01598 01599 cCiSession *cLlCiHandler::GetSessionBySessionId(int SessionId) 01600 { 01601 for (int i = 0; i < MAX_CI_SESSION; i++) { 01602 if (sessions[i] && sessions[i]->SessionId() == SessionId) 01603 return sessions[i]; 01604 } 01605 return NULL; 01606 } 01607 01608 cCiSession *cLlCiHandler::GetSessionByResourceId(int ResourceId, int Slot) 01609 { 01610 for (int i = 0; i < MAX_CI_SESSION; i++) { 01611 if (sessions[i] && sessions[i]->Tc()->Slot() == Slot && sessions[i]->ResourceId() == ResourceId) 01612 return sessions[i]; 01613 } 01614 return NULL; 01615 } 01616 01617 cCiSession *cLlCiHandler::CreateSession(int ResourceId) 01618 { 01619 if (!GetSessionByResourceId(ResourceId, tc->Slot())) { 01620 for (int i = 0; i < MAX_CI_SESSION; i++) { 01621 if (!sessions[i]) { 01622 switch (ResourceId) { 01623 case RI_RESOURCE_MANAGER: return sessions[i] = new cCiResourceManager(i + 1, tc); 01624 case RI_APPLICATION_INFORMATION: return sessions[i] = new cCiApplicationInformation(i + 1, tc); 01625 case RI_CONDITIONAL_ACCESS_SUPPORT: newCaSupport = true; 01626 return sessions[i] = new cCiConditionalAccessSupport(i + 1, tc); 01627 case RI_HOST_CONTROL: break; //XXX 01628 case RI_DATE_TIME: return sessions[i] = new cCiDateTime(i + 1, tc); 01629 case RI_MMI: return sessions[i] = new cCiMMI(i + 1, tc); 01630 } 01631 } 01632 } 01633 } 01634 return NULL; 01635 } 01636 01637 bool cLlCiHandler::OpenSession(int Length, const uint8_t *Data) 01638 { 01639 if (Length == 6 && *(Data + 1) == 0x04) { 01640 int ResourceId = ResourceIdToInt(Data + 2); 01641 dbgprotocol("OpenSession %08X\n", ResourceId); 01642 switch (ResourceId) { 01643 case RI_RESOURCE_MANAGER: 01644 case RI_APPLICATION_INFORMATION: 01645 case RI_CONDITIONAL_ACCESS_SUPPORT: 01646 case RI_HOST_CONTROL: 01647 case RI_DATE_TIME: 01648 case RI_MMI: 01649 { 01650 cCiSession *Session = CreateSession(ResourceId); 01651 if (Session) 01652 { 01653 Send(ST_OPEN_SESSION_RESPONSE, Session->SessionId(), 01654 Session->ResourceId(), SS_OK); 01655 return true; 01656 } 01657 esyslog("ERROR: can't create session for resource identifier: %08X", 01658 ResourceId); 01659 break; 01660 } 01661 default: esyslog("ERROR: unknown resource identifier: %08X", ResourceId); 01662 } 01663 } 01664 return false; 01665 } 01666 01667 bool cLlCiHandler::CloseSession(int SessionId) 01668 { 01669 dbgprotocol("CloseSession %08X\n", SessionId); 01670 cCiSession *Session = GetSessionBySessionId(SessionId); 01671 if (Session && sessions[SessionId - 1] == Session) { 01672 delete Session; 01673 sessions[SessionId - 1] = NULL; 01674 Send(ST_CLOSE_SESSION_RESPONSE, SessionId, 0, SS_OK); 01675 return true; 01676 } 01677 else { 01678 esyslog("ERROR: unknown session id: %d", SessionId); 01679 Send(ST_CLOSE_SESSION_RESPONSE, SessionId, 0, SS_NOT_ALLOCATED); 01680 } 01681 return false; 01682 } 01683 01684 int cLlCiHandler::CloseAllSessions(int Slot) 01685 { 01686 int result = 0; 01687 for (int i = 0; i < MAX_CI_SESSION; i++) { 01688 if (sessions[i] && sessions[i]->Tc()->Slot() == Slot) { 01689 CloseSession(sessions[i]->SessionId()); 01690 result++; 01691 } 01692 } 01693 return result; 01694 } 01695 01696 bool cLlCiHandler::Process(void) 01697 { 01698 bool result = true; 01699 cMutexLock MutexLock(&mutex); 01700 01701 for (int Slot = 0; Slot < numSlots; Slot++) 01702 { 01703 tc = tpl->Process(Slot); 01704 if (tc) 01705 { 01706 int Length; 01707 const uint8_t *Data = tc->Data(Length); 01708 if (Data && Length > 1) 01709 { 01710 switch (*Data) 01711 { 01712 case ST_SESSION_NUMBER: 01713 if (Length > 4) 01714 { 01715 int SessionId = ntohs(*(short *)&Data[2]); 01716 cCiSession *Session = GetSessionBySessionId(SessionId); 01717 if (Session) 01718 { 01719 Session->Process(Length - 4, Data + 4); 01720 if (Session->ResourceId() == RI_APPLICATION_INFORMATION) 01721 { 01722 #if 0 01723 esyslog("Test: %x", 01724 ((cCiApplicationInformation*)Session)->GetApplicationManufacturer()); 01725 #endif 01726 } 01727 } 01728 else 01729 esyslog("ERROR: unknown session id: %d", SessionId); 01730 } 01731 break; 01732 01733 case ST_OPEN_SESSION_REQUEST: 01734 OpenSession(Length, Data); 01735 break; 01736 01737 case ST_CLOSE_SESSION_REQUEST: 01738 if (Length == 4) 01739 CloseSession(ntohs(*(short *)&Data[2])); 01740 break; 01741 01742 case ST_CREATE_SESSION_RESPONSE: //XXX fall through to default 01743 case ST_CLOSE_SESSION_RESPONSE: //XXX fall through to default 01744 default: 01745 esyslog("ERROR: unknown session tag: %02X", *Data); 01746 } 01747 } 01748 } 01749 else if (CloseAllSessions(Slot)) 01750 { 01751 tpl->ResetSlot(Slot); 01752 result = false; 01753 } 01754 else if (tpl->ModuleReady(Slot)) 01755 { 01756 dbgprotocol("Module ready in slot %d\n", Slot); 01757 tpl->NewConnection(Slot); 01758 } 01759 } 01760 01761 bool UserIO = false; 01762 needCaPmt = false; 01763 for (int i = 0; i < MAX_CI_SESSION; i++) 01764 { 01765 if (sessions[i] && sessions[i]->Process()) 01766 { 01767 UserIO |= sessions[i]->HasUserIO(); 01768 if (sessions[i]->ResourceId() == RI_CONDITIONAL_ACCESS_SUPPORT) 01769 { 01770 cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *) sessions[i]; 01771 needCaPmt |= cas->NeedCaPmt(); 01772 } 01773 } 01774 } 01775 hasUserIO = UserIO; 01776 01777 if (newCaSupport) 01778 newCaSupport = result = false; // triggers new SetCaPmt at caller! 01779 return result; 01780 } 01781 01782 bool cLlCiHandler::EnterMenu(int Slot) 01783 { 01784 cMutexLock MutexLock(&mutex); 01785 cCiApplicationInformation *api = (cCiApplicationInformation *)GetSessionByResourceId(RI_APPLICATION_INFORMATION, Slot); 01786 return api ? api->EnterMenu() : false; 01787 } 01788 01789 cCiMenu *cLlCiHandler::GetMenu(void) 01790 { 01791 cMutexLock MutexLock(&mutex); 01792 for (int Slot = 0; Slot < numSlots; Slot++) { 01793 cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI, Slot); 01794 if (mmi) 01795 return mmi->Menu(); 01796 } 01797 return NULL; 01798 } 01799 01800 cCiEnquiry *cLlCiHandler::GetEnquiry(void) 01801 { 01802 cMutexLock MutexLock(&mutex); 01803 for (int Slot = 0; Slot < numSlots; Slot++) { 01804 cCiMMI *mmi = (cCiMMI *)GetSessionByResourceId(RI_MMI, Slot); 01805 if (mmi) 01806 return mmi->Enquiry(); 01807 } 01808 return NULL; 01809 } 01810 01811 const unsigned short *cLlCiHandler::GetCaSystemIds(int Slot) 01812 { 01813 cMutexLock MutexLock(&mutex); 01814 cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT, Slot); 01815 return cas ? cas->GetCaSystemIds() : NULL; 01816 } 01817 01818 bool cLlCiHandler::SetCaPmt(cCiCaPmt &CaPmt, int Slot) 01819 { 01820 cMutexLock MutexLock(&mutex); 01821 cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT, Slot); 01822 return cas && cas->SendPMT(CaPmt); 01823 } 01824 01825 void cLlCiHandler::SetTimeOffset(double offset_in_seconds) 01826 { 01827 cMutexLock MutexLock(&mutex); 01828 cCiDateTime *dt = NULL; 01829 01830 for (uint i = 0; i < (uint) NumSlots(); i++) 01831 { 01832 dt = (cCiDateTime*) GetSessionByResourceId(RI_DATE_TIME, i); 01833 if (dt) 01834 dt->SetTimeOffset(offset_in_seconds); 01835 } 01836 } 01837 01838 bool cLlCiHandler::Reset(int Slot) 01839 { 01840 cMutexLock MutexLock(&mutex); 01841 CloseAllSessions(Slot); 01842 return tpl->ResetSlot(Slot); 01843 } 01844 01845 bool cLlCiHandler::connected() const 01846 { 01847 return _connected; 01848 } 01849 01850 // -- cHlCiHandler ------------------------------------------------------------- 01851 01852 cHlCiHandler::cHlCiHandler(int Fd, int NumSlots) 01853 { 01854 numSlots = NumSlots; 01855 numCaSystemIds = 0; 01856 caSystemIds[0] = 0; 01857 fdCa = Fd; 01858 state = 0; 01859 esyslog("New High level CI handler"); 01860 } 01861 01862 cHlCiHandler::~cHlCiHandler() 01863 { 01864 cMutexLock MutexLock(&mutex); 01865 close(fdCa); 01866 } 01867 01868 int cHlCiHandler::CommHL(unsigned tag, unsigned function, struct ca_msg *msg) 01869 { 01870 if (tag) { 01871 msg->msg[2] = tag & 0xff; 01872 msg->msg[1] = (tag & 0xff00) >> 8; 01873 msg->msg[0] = (tag & 0xff0000) >> 16; 01874 esyslog("Sending message=[%02x %02x %02x ]", 01875 msg->msg[0], msg->msg[1], msg->msg[2]); 01876 } 01877 01878 return ioctl(fdCa, function, msg); 01879 } 01880 01881 int cHlCiHandler::GetData(unsigned tag, struct ca_msg *msg) 01882 { 01883 return CommHL(tag, CA_GET_MSG, msg); 01884 } 01885 01886 int cHlCiHandler::SendData(unsigned tag, struct ca_msg *msg) 01887 { 01888 return CommHL(tag, CA_SEND_MSG, msg); 01889 } 01890 01891 bool cHlCiHandler::Process(void) 01892 { 01893 cMutexLock MutexLock(&mutex); 01894 01895 struct ca_msg msg; 01896 switch(state) { 01897 case 0: 01898 // Get CA_system_ids 01899 /* Enquire */ 01900 if ((SendData(AOT_CA_INFO_ENQ, &msg)) < 0) { 01901 esyslog("HLCI communication failed"); 01902 } else { 01903 dbgprotocol("==> Ca Info Enquiry"); 01904 /* Receive */ 01905 if ((GetData(AOT_CA_INFO, &msg)) < 0) { 01906 esyslog("HLCI communication failed"); 01907 } else { 01908 QString message("Debug: "); 01909 for(int i = 0; i < 20; i++) { 01910 message += QString("%1 ").arg(msg.msg[i]); 01911 } 01912 LOG(VB_GENERAL, LOG_DEBUG, message); 01913 dbgprotocol("<== Ca Info"); 01914 int l = msg.msg[3]; 01915 const uint8_t *d = &msg.msg[4]; 01916 while (l > 1) { 01917 unsigned short id = ((unsigned short)(*d) << 8) | *(d + 1); 01918 dbgprotocol(" %04X", id); 01919 d += 2; 01920 l -= 2; 01921 if (numCaSystemIds < MAXCASYSTEMIDS) { 01922 caSystemIds[numCaSystemIds++] = id; 01923 caSystemIds[numCaSystemIds] = 0; 01924 } 01925 else 01926 esyslog("ERROR: too many CA system IDs!"); 01927 } 01928 dbgprotocol("\n"); 01929 } 01930 state = 1; 01931 break; 01932 } 01933 } 01934 01935 bool result = true; 01936 01937 return result; 01938 } 01939 01940 bool cHlCiHandler::EnterMenu(int) 01941 { 01942 return false; 01943 } 01944 01945 cCiMenu *cHlCiHandler::GetMenu(void) 01946 { 01947 return NULL; 01948 } 01949 01950 cCiEnquiry *cHlCiHandler::GetEnquiry(void) 01951 { 01952 return NULL; 01953 } 01954 01955 const unsigned short *cHlCiHandler::GetCaSystemIds(int) 01956 { 01957 return caSystemIds; 01958 } 01959 01960 bool cHlCiHandler::SetCaPmt(cCiCaPmt &CaPmt, int) 01961 { 01962 cMutexLock MutexLock(&mutex); 01963 struct ca_msg msg; 01964 01965 esyslog("Setting CA PMT."); 01966 state = 2; 01967 01968 msg.msg[3] = CaPmt.length; 01969 01970 if (CaPmt.length > (256 - 4)) 01971 { 01972 esyslog("CA message too long"); 01973 return false; 01974 } 01975 01976 memcpy(&msg.msg[4], CaPmt.capmt, CaPmt.length); 01977 01978 if ((SendData(AOT_CA_PMT, &msg)) < 0) { 01979 esyslog("HLCI communication failed"); 01980 return false; 01981 } 01982 01983 return true; 01984 } 01985 01986 bool cHlCiHandler::Reset(int) 01987 { 01988 if ((ioctl(fdCa, CA_RESET)) < 0) { 01989 esyslog("ioctl CA_RESET failed."); 01990 return false; 01991 } 01992 return true; 01993 } 01994 01995 bool cHlCiHandler::NeedCaPmt(void) 01996 { 01997 if(state == 1) 01998 return true; 01999 02000 return false; 02001 }
1.7.6.1