00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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
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
00069
00070
00071
00072 #define WRKRND_TIME_BEFORE_ENTER_MENU 15 // seconds
00073
00074
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
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
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
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;
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;
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--;
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
00356 return data[size - 1];
00357 }
00358 return 0;
00359 }
00360
00361
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
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
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
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
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
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
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 ;
00641 }
00642 switch (Tc->LastResponse()) {
00643 case T_REQUEST_TC:
00644
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
00657 return NULL;
00658 break;
00659 }
00660
00661 return Tc;
00662 break;
00663 default: ;
00664 }
00665 }
00666 }
00667 return NULL;
00668 }
00669
00670
00671
00672
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
00683
00684 #define SS_OK 0x00
00685 #define SS_NOT_ALLOCATED 0xF0
00686
00687
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
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
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
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;
00968 }
00969 return false;
00970 }
00971
00972
00973
00974 class cCiConditionalAccessSupport : public cCiSession {
00975 private:
00976 int state;
00977 int numCaSystemIds;
00978 unsigned short caSystemIds[MAXCASYSTEMIDS + 1];
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
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
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
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
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
01140
01141
01142
01143 #define CLOSE_MMI_IMMEDIATE 0x00
01144 #define CLOSE_MMI_DELAY 0x01
01145
01146
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
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
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
01172
01173 #define EF_BLIND 0x01
01174
01175
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
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
01286 l--;
01287 enquiry->blind = blind & EF_BLIND;
01288 enquiry->expectedLength = *d++;
01289 l--;
01290
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
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]; };
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
01355 return true;
01356 }
01357
01358
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
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
01426
01427
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;
01438 capmt[length++] = (ProgramNumber >> 8) & 0xFF;
01439 capmt[length++] = ProgramNumber & 0xFF;
01440 capmt[length++] = 0x01;
01441
01442
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
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
01499 if (infoLengthPos + 2 == length)
01500 capmt[length++] = CPCI_OK_DESCRAMBLING;
01501
01502 capmt[length++] = 0x09;
01503 capmt[length++] = 4 + data_len;
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
01517 int l = length - infoLengthPos - 2;
01518 capmt[infoLengthPos] = (l >> 8) & 0xFF;
01519 capmt[infoLengthPos + 1] = l & 0xFF;
01520 }
01521
01522
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;
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;
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;
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:
01743 case ST_CLOSE_SESSION_RESPONSE:
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;
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
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
01899
01900 if ((SendData(AOT_CA_INFO_ENQ, &msg)) < 0) {
01901 esyslog("HLCI communication failed");
01902 } else {
01903 dbgprotocol("==> Ca Info Enquiry");
01904
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 }