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