SASC
DVBSoftCAM

cam.c

Go to the documentation of this file.
00001 /*
00002  * Softcam plugin to VDR (C++)
00003  *
00004  * This code is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This code is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
00018  */
00019 
00020 #include <stdlib.h>
00021 #include <stdio.h>
00022 #include <errno.h>
00023 
00024 #include <vdr/device.h>
00025 #include <vdr/channels.h>
00026 #include <vdr/thread.h>
00027 #ifndef SASC
00028 #include <vdr/ringbuffer.h>
00029 #endif
00030 
00031 #include "cam.h"
00032 #include "global.h"
00033 #include "device.h"
00034 #include "scsetup.h"
00035 #include "filter.h"
00036 #include "system.h"
00037 #include "data.h"
00038 #include "override.h"
00039 #include "misc.h"
00040 #include "log-core.h"
00041 #ifndef SASC
00042 #include "FFdecsa/FFdecsa.h"
00043 #endif
00044 
00045 #define IDLE_SLEEP          0 // idleTime when sleeping
00046 #define IDLE_GETCA        200 // idleTime when waiting for ca descriptors
00047 #define IDLE_GETCA_SLOW 20000 // idleTime if no enc. system
00048 #define IDLE_NO_SYNC      800 // idleTime when not in sync
00049 #define IDLE_SYNC        2000 // idleTime when in sync
00050 
00051 #define CW_REPEAT_TIME   2000 // rewrite CW after X ms
00052 #define LOG_COUNT           3 // stop logging after X complete ECM cycles
00053 #define CHAIN_HOLD     120000 // min. time to hold a logger chain
00054 #define ECM_DATA_TIME    6000 // time to wait for ECM data updates
00055 #define MAX_ECM_IDLE   300000 // delay before an idle handler can be removed
00056 #define MAX_ECM_HOLD    15000 // delay before an idle handler stops processing
00057 
00058 #define ECMCACHE_FILE "ecm.cache"
00059 
00060 #define L_HEX       2
00061 #define L_HEX_ECM   LCLASS(L_HEX,2)
00062 #define L_HEX_EMM   LCLASS(L_HEX,4)
00063 #define L_HEX_CAT   LCLASS(L_HEX,8)
00064 #define L_HEX_PMT   LCLASS(L_HEX,16)
00065 #define L_HEX_HOOK  LCLASS(L_HEX,32)
00066 #define L_HEX_ALL   LALL(L_HEX_HOOK)
00067 
00068 static const struct LogModule lm_hex = {
00069   (LMOD_ENABLE|L_HEX_ALL)&LOPT_MASK,
00070   (LMOD_ENABLE)&LOPT_MASK,
00071   "hexdata",
00072   { "ecm","emm","cat","pmt","hook" }
00073   };
00074 ADD_MODULE(L_HEX,lm_hex)
00075 
00076 static const char *typeNames[] = { "typ0","typ1","VIDEO","typ3","AUDIO","typ5","DOLBY","typ6+" };
00077 #define TYPENAME(type) (typeNames[(type)<=7?(type):7])
00078 
00079 // -- cLogStats ---------------------------------------------------------------
00080 
00081 #define COUNTS  20
00082 #define SAMPLE (30*1000)
00083 #define AVR1   (60*1000)
00084 #define AVR2   (4*60*1000)
00085 #define AVR3   (10*60*1000)
00086 #define REPORT (60*1000)
00087 
00088 class cLogStats : public cThread {
00089 private:
00090   cTimeMs sTime, repTime;
00091   int sCount, sIdx, sCounts[COUNTS][2];
00092 protected:
00093   virtual void Action(void);
00094 public:
00095   cLogStats(void);
00096   ~cLogStats();
00097   void Count(void);
00098   };
00099 
00100 static cMutex logstatsMutex;
00101 static cLogStats *logstats=0;
00102 
00103 void LogStatsUp(void)
00104 {
00105   logstatsMutex.Lock();
00106   if(LOG(L_CORE_AUSTATS) && !logstats) logstats=new cLogStats;
00107   logstatsMutex.Unlock();
00108 }
00109 
00110 void LogStatsDown(void)
00111 {
00112   logstatsMutex.Lock();
00113   if(logstats) { delete logstats; logstats=0; }
00114   logstatsMutex.Unlock();
00115 }
00116 
00117 cLogStats::cLogStats(void)
00118 {
00119   sCount=sIdx=0;
00120   for(int i=0; i<COUNTS; i++) { sCounts[i][0]=0; sCounts[i][1]=SAMPLE; }
00121   SetDescription("logger stats");
00122   Start();
00123 }
00124 
00125 cLogStats::~cLogStats()
00126 {
00127   Cancel(2);
00128 }
00129 
00130 void cLogStats::Count(void)
00131 {
00132   sCount++;
00133 }
00134 
00135 void cLogStats::Action(void)
00136 {
00137   while(Running()) {
00138     cCondWait::SleepMs(50);
00139     if(sTime.Elapsed()>SAMPLE) {
00140       sCounts[sIdx][0]=sCount;          sCount=0;
00141       sCounts[sIdx][1]=sTime.Elapsed(); sTime.Set();
00142       if(++sIdx >= COUNTS) sIdx=0;
00143       }
00144     if(repTime.Elapsed()>REPORT) {
00145       repTime.Set();
00146       if(sCounts[(sIdx+COUNTS-1)%COUNTS][0]>0) {
00147         LBSTART(L_CORE_AUSTATS);
00148         LBPUT("EMM packet load average (%d/%d/%dmin)",AVR1/60000,AVR2/60000,AVR3/60000);
00149         int s=0, t=0;
00150         for(int i=1; i<=COUNTS; i++) {
00151           s+=sCounts[(sIdx+COUNTS-i)%COUNTS][0];
00152           t+=sCounts[(sIdx+COUNTS-i)%COUNTS][1];
00153           if(i==(AVR1/SAMPLE) || i==(AVR2/SAMPLE) || i==(AVR3/SAMPLE))
00154             LBPUT(" %4d",(int)((float)s/(float)t*1000.0));
00155           }
00156         LBPUT(" pks/s");
00157         LBEND();
00158         }
00159       }
00160     }
00161 }
00162 
00163 // -- cHookManager -------------------------------------------------------------
00164 
00165 class cHookManager : public cAction {
00166   const char *devId;
00167   cSimpleList<cLogHook> hooks;
00168   //
00169   cPidFilter *AddFilter(int Pid, int Section, int Mask, int IdleTime);
00170   void ClearHooks(void);
00171   void DelHook(cLogHook *hook);
00172 protected:
00173   virtual void Process(cPidFilter *filter, unsigned char *data, int len);
00174 public:
00175   cHookManager(cDevice *Device, const char *DevId);
00176   virtual ~cHookManager();
00177   void AddHook(cLogHook *hook);
00178   bool TriggerHook(int id);
00179   void Down(void);
00180   };
00181 
00182 cHookManager::cHookManager(cDevice *Device, const char *DevId)
00183 :cAction("hookmanager",Device,DevId)
00184 {
00185   devId=DevId;
00186   Priority(10);
00187 }
00188 
00189 cHookManager::~cHookManager()
00190 {
00191   Down();
00192 }
00193 
00194 void cHookManager::Down(void)
00195 {
00196   Lock();
00197   while(cLogHook *hook=hooks.First()) DelHook(hook);
00198   DelAllFilter();
00199   Unlock();
00200 }
00201 
00202 bool cHookManager::TriggerHook(int id)
00203 {
00204   Lock();
00205   for(cLogHook *hook=hooks.First(); hook; hook=hooks.Next(hook))
00206     if(hook->id==id) {
00207       hook->delay.Set(CHAIN_HOLD);
00208       Unlock();
00209       return true;
00210       }
00211   Unlock();
00212   return false;
00213 }
00214 
00215 void cHookManager::AddHook(cLogHook *hook)
00216 {
00217   Lock();
00218   PRINTF(L_CORE_HOOK,"%s: starting hook '%s' (%04x)",devId,hook->name,hook->id);
00219   hook->delay.Set(CHAIN_HOLD);
00220   hooks.Add(hook);
00221   for(cPid *pid=hook->pids.First(); pid; pid=hook->pids.Next(pid)) {
00222     cPidFilter *filter=AddFilter(pid->pid,pid->sct,pid->mask,CHAIN_HOLD/8);
00223     if(filter) {
00224       filter->userData=(void *)hook;
00225       pid->filter=filter;
00226       }
00227     }
00228   Unlock();
00229 }
00230 
00231 void cHookManager::DelHook(cLogHook *hook)
00232 {
00233   PRINTF(L_CORE_HOOK,"%s: stopping hook '%s' (%04x)",devId,hook->name,hook->id);
00234   for(cPid *pid=hook->pids.First(); pid; pid=hook->pids.Next(pid)) {
00235     cPidFilter *filter=pid->filter;
00236     if(filter) {
00237       DelFilter(filter);
00238       pid->filter=0;
00239       }
00240     }
00241   hooks.Del(hook);
00242 }
00243 
00244 cPidFilter *cHookManager::AddFilter(int Pid, int Section, int Mask, int IdleTime)
00245 {
00246   cPidFilter *filter=NewFilter(IdleTime);
00247   if(filter) {
00248     filter->SetBuffSize(32768);
00249     filter->Start(Pid,Section,Mask);
00250     PRINTF(L_CORE_HOOK,"%s: added filter pid=0x%.4x sct=0x%.2x/0x%.2x idle=%d",devId,Pid,Section,Mask,IdleTime);
00251     }
00252   else PRINTF(L_GEN_ERROR,"no free slot or filter failed to open for hookmanager %s",devId);
00253   return filter;
00254 }
00255 
00256 void cHookManager::Process(cPidFilter *filter, unsigned char *data, int len)
00257 {
00258   if(data && len>0) {
00259     HEXDUMP(L_HEX_HOOK,data,len,"HOOK pid 0x%04x",filter->Pid());
00260     if(SCT_LEN(data)==len) {
00261       cLogHook *hook=(cLogHook *)(filter->userData);
00262       if(hook) {
00263         hook->Process(filter->Pid(),data);
00264         if(hook->bailOut || hook->delay.TimedOut()) DelHook(hook);
00265         }
00266       }
00267     else PRINTF(L_CORE_HOOK,"%s: incomplete section %d != %d",devId,len,SCT_LEN(data));
00268     }
00269   else {
00270     cLogHook *hook=(cLogHook *)(filter->userData);
00271     if(hook && (hook->bailOut || hook->delay.TimedOut())) DelHook(hook);
00272     }
00273 }
00274 
00275 // -- cLogChain ----------------------------------------------------------------
00276 
00277 class cLogChain : public cSimpleItem {
00278 public:
00279   cCam *cam;
00280   const char *devId;
00281   int caid, source, transponder;
00282   bool softCSA, active, delayed;
00283   cTimeMs delay;
00284   cPids pids;
00285   cSimpleList<cSystem> systems;
00286   //
00287   cLogChain(cCam *Cam, const char *DevId, bool soft, int src, int tr);
00288   void Process(int pid, const unsigned char *data);
00289   bool Parse(const unsigned char *cat);
00290   };
00291 
00292 cLogChain::cLogChain(cCam *Cam, const char *DevId, bool soft, int src, int tr)
00293 {
00294   cam=Cam; devId=DevId; softCSA=soft; source=src; transponder=tr;
00295   active=delayed=false;
00296 }
00297 
00298 void cLogChain::Process(int pid, const unsigned char *data)
00299 {
00300   if(active) {
00301     for(cSystem *sys=systems.First(); sys; sys=systems.Next(sys))
00302       sys->ProcessEMM(pid,caid,data);
00303     }
00304 }
00305 
00306 bool cLogChain::Parse(const unsigned char *cat)
00307 {
00308   if(cat[0]==0x09) {
00309     caid=WORD(cat,2,0xFFFF);
00310     LBSTARTF(L_CORE_AU);
00311     LBPUT("%s: chain caid %04x",devId,caid);
00312     cSystem *sys;
00313     if(systems.Count()>0) {
00314       LBPUT(" ++");
00315       for(sys=systems.First(); sys; sys=systems.Next(sys))
00316         sys->ParseCAT(&pids,cat,source,transponder);
00317       }
00318     else {
00319       LBPUT(" ->");
00320       if(!overrides.Ignore(source,transponder,caid)) {
00321         int Pri=0;
00322         while((sys=cSystems::FindBySysId(caid,!softCSA,Pri))) {
00323           Pri=sys->Pri();
00324           if(sys->HasLogger()) {
00325             sys->SetOwner(cam);
00326             sys->ParseCAT(&pids,cat,source,transponder);
00327             systems.Add(sys);
00328             LBPUT(" %s(%d)",sys->Name(),sys->Pri());
00329             }
00330           else
00331             delete sys;
00332           }
00333         }
00334       }
00335     if(systems.Count()==0) LBPUT(" none available");
00336     for(cPid *pid=pids.First(); pid; pid=pids.Next(pid))
00337        LBPUT(" [%04x-%02x/%02x]",pid->pid,pid->sct,pid->mask);
00338     LBEND();
00339     if(systems.Count()>0 && pids.Count()>0)
00340       return true;
00341     }
00342   return false;
00343 }
00344 
00345 // -- cLogger ------------------------------------------------------------------
00346 
00347 class cLogger : public cAction {
00348 private:
00349   cCam *cam;
00350   const char *devId;
00351   bool softCSA, up;
00352   cSimpleList<cLogChain> chains;
00353   cSimpleList<cEcmInfo> active;
00354   //
00355   cPidFilter *catfilt;
00356   int catVers;
00357   int source, transponder;
00358   //
00359   enum ePreMode { pmNone, pmStart, pmWait, pmActive, pmStop };
00360   ePreMode prescan;
00361   cTimeMs pretime;
00362   //
00363   cPidFilter *AddFilter(int Pid, int Section, int Mask, int IdleTime);
00364   void SetChains(void);
00365   void ClearChains(void);
00366   void StartChain(cLogChain *chain);
00367   void StopChain(cLogChain *chain, bool force);
00368   void ProcessCat(unsigned char *data, int len);
00369 protected:
00370   virtual void Process(cPidFilter *filter, unsigned char *data, int len);
00371 public:
00372   cLogger(cCam *Cam, cDevice *Device, const char *DevId, bool soft);
00373   virtual ~cLogger();
00374   void EcmStatus(const cEcmInfo *ecm, bool on);
00375   void Up(void);
00376   void Down(void);
00377   void PreScan(int src, int tr);
00378   };
00379 
00380 cLogger::cLogger(cCam *Cam, cDevice *Device, const char *DevId, bool soft)
00381 :cAction("logger",Device,DevId)
00382 {
00383   cam=Cam; devId=DevId; softCSA=soft;
00384   catfilt=0; up=false; prescan=pmNone;
00385   Priority(10);
00386 }
00387 
00388 cLogger::~cLogger()
00389 {
00390   Down();
00391 }
00392 
00393 void cLogger::Up(void)
00394 {
00395   Lock();
00396   if(!up) {
00397     PRINTF(L_CORE_AUEXTRA,"%s: UP",devId);
00398     catVers=-1;
00399     catfilt=AddFilter(1,0x01,0xFF,0);
00400     up=true;
00401     }
00402   Unlock();
00403 }
00404 
00405 void cLogger::Down(void)
00406 {
00407   Lock();
00408   if(up) {
00409     PRINTF(L_CORE_AUEXTRA,"%s: DOWN",devId);
00410     ClearChains();
00411     DelAllFilter();
00412     catfilt=0; up=false; prescan=pmNone;
00413     }
00414   Unlock();
00415 }
00416 
00417 void cLogger::PreScan(int src, int tr)
00418 {
00419   Lock();
00420   source=src; transponder=tr;
00421   prescan=pmStart; Up();
00422   Unlock();
00423 }
00424 
00425 void cLogger::EcmStatus(const cEcmInfo *ecm, bool on)
00426 {
00427   Lock();
00428   PRINTF(L_CORE_AUEXTRA,"%s: ecm prgid=%d caid=%04x prov=%.4x %s",devId,ecm->prgId,ecm->caId,ecm->provId,on ? "active":"inactive");
00429   source=ecm->source; transponder=ecm->transponder;
00430   cEcmInfo *e;
00431   if(on) {
00432     e=new cEcmInfo(ecm);
00433     active.Add(e);
00434     if(!up) Up();
00435     }
00436   else {
00437     for(e=active.First(); e; e=active.Next(e))
00438       if(e->Compare(ecm)) {
00439         active.Del(e);
00440         break;
00441         }
00442     }
00443   if(prescan>=pmWait) prescan=pmStop;
00444   SetChains();
00445   prescan=pmNone;
00446   Unlock();
00447 }
00448 
00449 void cLogger::SetChains(void)
00450 {
00451   for(cLogChain *chain=chains.First(); chain; chain=chains.Next(chain)) {
00452     bool act=false;
00453     if(ScSetup.AutoUpdate>1 || prescan==pmActive) act=true;
00454     else if(ScSetup.AutoUpdate==1) {
00455       for(cEcmInfo *e=active.First(); e; e=active.Next(e))
00456         if((e->emmCaId && chain->caid==e->emmCaId) || chain->caid==e->caId) {
00457           act=true; break;
00458           }
00459        }
00460     if(act) StartChain(chain);
00461     else StopChain(chain,prescan==pmStop);
00462     }
00463 }
00464 
00465 void cLogger::ClearChains(void)
00466 {
00467   for(cLogChain *chain=chains.First(); chain; chain=chains.Next(chain))
00468     StopChain(chain,true);
00469   chains.Clear();
00470 }
00471 
00472 void cLogger::StartChain(cLogChain *chain)
00473 {
00474   if(chain->delayed)
00475     PRINTF(L_CORE_AUEXTRA,"%s: restarting delayed chain %04x",devId,chain->caid);
00476   chain->delayed=false;
00477   if(!chain->active) {
00478     PRINTF(L_CORE_AU,"%s: starting chain %04x",devId,chain->caid);
00479     chain->active=true;
00480     for(cPid *pid=chain->pids.First(); pid; pid=chain->pids.Next(pid)) {
00481       cPidFilter *filter=AddFilter(pid->pid,pid->sct,pid->mask,CHAIN_HOLD/8);
00482       if(filter) {
00483         filter->userData=(void *)chain;
00484         pid->filter=filter;
00485         }
00486       }
00487     }
00488 }
00489 
00490 void cLogger::StopChain(cLogChain *chain, bool force)
00491 {
00492   if(chain->active) {
00493     if(force || (chain->delayed && chain->delay.TimedOut())) {
00494       PRINTF(L_CORE_AU,"%s: stopping chain %04x",devId,chain->caid);
00495       chain->active=false;
00496       for(cPid *pid=chain->pids.First(); pid; pid=chain->pids.Next(pid)) {
00497         cPidFilter *filter=pid->filter;
00498         if(filter) {
00499           DelFilter(filter);
00500           pid->filter=0;
00501           }
00502         }
00503       }
00504     else if(!chain->delayed) {
00505       PRINTF(L_CORE_AUEXTRA,"%s: delaying chain %04x",devId,chain->caid);
00506       chain->delayed=true;
00507       chain->delay.Set(CHAIN_HOLD);
00508       }
00509     }
00510 }
00511 
00512 cPidFilter *cLogger::AddFilter(int Pid, int Section, int Mask, int IdleTime)
00513 {
00514   cPidFilter *filter=NewFilter(IdleTime);
00515   if(filter) {
00516     if(Pid>1) filter->SetBuffSize(KILOBYTE(64));
00517     filter->Start(Pid,Section,Mask);
00518     PRINTF(L_CORE_AUEXTRA,"%s: added filter pid=0x%.4x sct=0x%.2x/0x%.2x idle=%d",devId,Pid,Section,Mask,IdleTime);
00519     }
00520   else PRINTF(L_GEN_ERROR,"no free slot or filter failed to open for logger %s",devId);
00521   return filter;
00522 }
00523 
00524 void cLogger::ProcessCat(unsigned char *data, int len)
00525 {
00526   for(int i=0; i<len; i+=data[i+1]+2) {
00527     if(data[i]==0x09) {
00528       int caid=WORD(data,i+2,0xFFFF);
00529       cLogChain *chain;
00530       for(chain=chains.First(); chain; chain=chains.Next(chain))
00531         if(chain->caid==caid) break;
00532       if(chain)
00533         chain->Parse(&data[i]);
00534       else {
00535         chain=new cLogChain(cam,devId,softCSA,source,transponder);
00536         if(chain->Parse(&data[i]))
00537           chains.Add(chain);
00538         else
00539           delete chain;
00540         }
00541       }
00542     }
00543 }
00544 
00545 void cLogger::Process(cPidFilter *filter, unsigned char *data, int len)
00546 {
00547   if(data && len>0) {
00548     if(filter==catfilt) {
00549       int vers=(data[5]&0x3E)>>1;
00550       if(data[0]==0x01 && vers!=catVers) {
00551         PRINTF(L_CORE_AUEXTRA,"%s: got CAT version %02x",devId,vers);
00552         catVers=vers;
00553         HEXDUMP(L_HEX_CAT,data,len,"CAT vers %02x",catVers);
00554         ClearChains();
00555         ProcessCat(&data[8],len-4-8);
00556         unsigned char buff[2048];
00557         if((len=overrides.GetCat(source,transponder,buff,sizeof(buff)))>0) {
00558           HEXDUMP(L_HEX_CAT,buff,len,"override CAT");
00559           ProcessCat(buff,len);
00560           }
00561         SetChains();
00562         if(prescan==pmStart) { prescan=pmWait; pretime.Set(2000); }
00563         }
00564       if(prescan==pmWait && pretime.TimedOut()) { prescan=pmActive; SetChains(); }
00565       }
00566     else {
00567       HEXDUMP(L_HEX_EMM,data,len,"EMM pid 0x%04x",filter->Pid());
00568       if(logstats) logstats->Count();
00569       if(SCT_LEN(data)==len) {
00570         cLogChain *chain=(cLogChain *)(filter->userData);
00571         if(chain) {
00572           chain->Process(filter->Pid(),data);
00573           if(chain->delayed) StopChain(chain,false);
00574           }
00575         }
00576       else PRINTF(L_CORE_AU,"%s: incomplete section %d != %d",devId,len,SCT_LEN(data));
00577       }
00578     }
00579   else {
00580     cLogChain *chain=(cLogChain *)(filter->userData);
00581     if(chain && chain->delayed) StopChain(chain,false);
00582     }
00583 }
00584 
00585 // -- cEcmData -----------------------------------------------------------------
00586 
00587 #define CACHE_VERS 1
00588 
00589 class cEcmData : public cEcmInfo {
00590 public:
00591   cEcmData(void):cEcmInfo() {}
00592   cEcmData(cEcmInfo *e):cEcmInfo(e) {}
00593   virtual cString ToString(bool hide);
00594   bool Parse(const char *buf);
00595   };
00596 
00597 bool cEcmData::Parse(const char *buf)
00598 {
00599   char Name[64];
00600   int nu=0, num, vers=0;
00601   Name[0]=0;
00602   if(sscanf(buf,"V%d:%d:%x:%x:%63[^:]:%x/%x:%x:%x/%x:%d:%d/%d%n",
00603              &vers,&grPrgId,&source,&transponder,Name,&caId,&emmCaId,&provId,
00604              &ecm_pid,&ecm_table,&rewriterId,&nu,&dataIdx,&num)>=13
00605      && vers==CACHE_VERS) {
00606     SetName(Name);
00607     SetRewriter();
00608     prgId=grPrgId%SIDGRP_SHIFT;
00609     const char *line=buf+num;
00610     if(nu>0 && *line++==':') {
00611       unsigned char *dat=AUTOMEM(nu);
00612       if(GetHex(line,dat,nu,true)==nu && dat[0]==0x09 && dat[1]==nu-2)
00613         AddCaDescr(dat,nu);
00614       }
00615     return true;
00616     }
00617   return false;
00618 }
00619 
00620 cString cEcmData::ToString(bool hide)
00621 {
00622   char *str;
00623   if(caDescr) {
00624     str=AUTOARRAY(char,caDescrLen*2+16);
00625     int q=sprintf(str,"%d/%d:",caDescrLen,dataIdx);
00626     HexStr(str+q,caDescr,caDescrLen);
00627     }
00628   else {
00629     str=AUTOARRAY(char,10);
00630     sprintf(str,"0/%d:",dataIdx);
00631     }
00632   return cString::sprintf("V%d:%d:%x:%x:%s:%x/%x:%x:%x/%x:%d:%s",
00633                             CACHE_VERS,grPrgId,source,transponder,name,
00634                             caId,emmCaId,provId,ecm_pid,ecm_table,rewriterId,
00635                             str);
00636 }
00637 
00638 // -- cEcmCache ----------------------------------------------------------------
00639 
00640 cEcmCache ecmcache;
00641 
00642 cEcmCache::cEcmCache(void)
00643 :cStructListPlain<cEcmData>("ecm cache",ECMCACHE_FILE,SL_READWRITE|SL_MISSINGOK)
00644 {}
00645 
00646 void cEcmCache::New(cEcmInfo *e)
00647 {
00648   if(ScSetup.EcmCache>0) return;
00649   ListLock(true);
00650   cEcmData *dat;
00651   if(!(dat=Exists(e))) {
00652     dat=new cEcmData(e);
00653     Add(dat);
00654     Modified();
00655     PRINTF(L_CORE_ECM,"cache add prgId=%d source=%x transponder=%x ecm=%x/%x",e->grPrgId,e->source,e->transponder,e->ecm_pid,e->ecm_table);
00656     }
00657   else {
00658     if(strcasecmp(e->name,dat->name)) {
00659       dat->SetName(e->name);
00660       Modified();
00661       }
00662     if(dat->AddCaDescr(e))
00663       Modified();
00664     }
00665   ListUnlock();
00666   e->SetCached();
00667 }
00668 
00669 cEcmData *cEcmCache::Exists(cEcmInfo *e)
00670 {
00671   cEcmData *dat;
00672   for(dat=First(); dat; dat=Next(dat))
00673     if(dat->Compare(e)) break;
00674   return dat;
00675 }
00676 
00677 int cEcmCache::GetCached(cSimpleList<cEcmInfo> *list, int sid, int Source, int Transponder)
00678 {
00679   int n=0;
00680   list->Clear();
00681   if(ScSetup.EcmCache>1) return 0;
00682   ListLock(false);
00683   for(cEcmData *dat=First(); dat; dat=Next(dat)) {
00684     if(dat->grPrgId==sid && dat->source==Source && dat->transponder==Transponder) {
00685       cEcmInfo *e=new cEcmInfo(dat);
00686       if(e) {
00687         PRINTF(L_CORE_ECM,"from cache: system %s (%04x) id %04x with ecm %x/%x",e->name,e->caId,e->provId,e->ecm_pid,e->ecm_table);
00688         e->SetCached();
00689         list->Add(e);
00690         n++;
00691         }
00692       }
00693     }
00694   ListUnlock();
00695   return n;
00696 }
00697 
00698 void cEcmCache::Delete(cEcmInfo *e)
00699 {
00700   if(ScSetup.EcmCache>0) return;
00701   ListLock(false);
00702   cEcmData *dat=Exists(e);
00703   ListUnlock();
00704   if(dat) {
00705     DelItem(dat);
00706     PRINTF(L_CORE_ECM,"invalidated cached prgId=%d source=%x transponder=%x ecm=%x/%x",dat->grPrgId,dat->source,dat->transponder,dat->ecm_pid,dat->ecm_table);
00707     }
00708 }
00709 
00710 void cEcmCache::Flush(void)
00711 {
00712   ListLock(true);
00713   Clear();
00714   Modified();
00715   PRINTF(L_CORE_ECM,"cache flushed");
00716   ListUnlock();
00717 }
00718 
00719 bool cEcmCache::ParseLinePlain(const char *line)
00720 {
00721   cEcmData *dat=new cEcmData;
00722   if(dat && dat->Parse(line) && !Exists(dat)) Add(dat);
00723   else delete dat;
00724   return true;
00725 }
00726 
00727 // -- cCaDescr -----------------------------------------------------------------
00728 
00729 cCaDescr::cCaDescr(void)
00730 {
00731   descr=0; len=0;
00732 }
00733 
00734 cCaDescr::cCaDescr(const cCaDescr &cd)
00735 {
00736   descr=0; len=0;
00737   Set(cd.descr,cd.len);
00738 }
00739 
00740 cCaDescr::~cCaDescr()
00741 {
00742   Clear();
00743 }
00744 
00745 const unsigned char *cCaDescr::Get(int &l) const
00746 {
00747   l=len;
00748   return descr;
00749 }
00750 
00751 void cCaDescr::Set(const cCaDescr *d)
00752 {
00753   Set(d->descr,d->len);
00754 }
00755 
00756 void cCaDescr::Set(const unsigned char *de, int l)
00757 {
00758   Clear();
00759   if(l) {
00760     descr=MALLOC(unsigned char,l);
00761     if(descr) {
00762       memcpy(descr,de,l);
00763       len=l;
00764       }
00765     }
00766 }
00767 
00768 void cCaDescr::Clear(void)
00769 {
00770   free(descr); descr=0; len=0;
00771 }
00772 
00773 void cCaDescr::Join(const cCaDescr *cd, bool rev)
00774 {
00775   if(cd->descr) {
00776     int l=len+cd->len;
00777     unsigned char *m=MALLOC(unsigned char,l);
00778     if(m) {
00779       if(!rev) {
00780         if(descr) memcpy(m,descr,len);
00781         memcpy(m+len,cd->descr,cd->len);
00782         }
00783       else {
00784         memcpy(m,cd->descr,cd->len);
00785         if(descr) memcpy(m+cd->len,descr,len);
00786         }
00787       Clear();
00788       descr=m; len=l;
00789       }
00790     }
00791 }
00792 
00793 bool cCaDescr::operator== (const cCaDescr &cd) const
00794 {
00795   return len==cd.len && (len==0 || memcmp(descr,cd.descr,len)==0);
00796 }
00797 
00798 cString cCaDescr::ToString(void)
00799 {
00800   if(!descr) return "<empty>";
00801   char *str=AUTOARRAY(char,len*3+8);
00802   int q=sprintf(str,"%02X",descr[0]);
00803   for(int i=1; i<len; i++) q+=sprintf(str+q," %02X",descr[i]);
00804   return str;
00805 }
00806 
00807 // -- cPrg ---------------------------------------------------------------------
00808 
00809 cPrg::cPrg(void)
00810 {
00811   Setup();
00812 }
00813 
00814 cPrg::cPrg(int Sid, bool IsUpdate)
00815 {
00816   Setup();
00817   sid=Sid; isUpdate=IsUpdate;
00818 }
00819 
00820 void cPrg::Setup(void)
00821 {
00822   sid=-1; source=-1; transponder=-1;
00823   isUpdate=pidCaDescr=false;
00824 }
00825 
00826 void cPrg::DumpCaDescr(int c)
00827 {
00828   PRINTF(c,"prgca: %s",*caDescr.ToString());
00829   for(cPrgPid *pid=pids.First(); pid; pid=pids.Next(pid))
00830     PRINTF(c,"pidca %04x: %s",pid->pid,*pid->caDescr.ToString());
00831 }
00832 
00833 bool cPrg::SimplifyCaDescr(void)
00834 {
00835   if(HasPidCaDescr()) {
00836     bool equal=true;
00837     if(pids.Count()>1) {
00838       cPrgPid *pid0=pids.First();
00839       for(cPrgPid *pid1=pids.Next(pid0); pid1; pid1=pids.Next(pid1))
00840         if(!(pid0->caDescr==pid1->caDescr)) { equal=false; break; }
00841       }
00842     if(equal) {
00843       cPrgPid *pid=pids.First();
00844       caDescr.Join(&pid->caDescr);
00845       for(; pid; pid=pids.Next(pid)) pid->caDescr.Clear();
00846       SetPidCaDescr(false);
00847       }
00848     }
00849   if(HasPidCaDescr()) {
00850     for(cPrgPid *pid=pids.First(); pid; pid=pids.Next(pid))
00851       pid->caDescr.Join(&caDescr,true);
00852     caDescr.Clear();
00853     }
00854 
00855   PRINTF(L_CORE_PIDS,"Ca descriptors after simplify (pidCa=%d)",HasPidCaDescr());
00856   DumpCaDescr(L_CORE_PIDS);
00857 
00858   return HasPidCaDescr();
00859 }
00860 
00861 // -- cEcmPri ------------------------------------------------------------------
00862 
00863 class cEcmPri : public cSimpleItem {
00864 public:
00865   cEcmInfo *ecm;
00866   int pri, sysIdent;
00867   };
00868 
00869 // -- cEcmHandler --------------------------------------------------------------
00870 
00871 class cEcmHandler : public cSimpleItem, public cAction {
00872 private:
00873   const char *devId;
00874   int cwIndex;
00875   cCam *cam;
00876   char *id;
00877   cTimeMs idleTime;
00878   //
00879   cMutex dataMutex;
00880   cPrg prg;
00881   //
00882   cSystem *sys;
00883   cPidFilter *filter;
00884   int filterCwIndex, filterSource, filterTransponder, filterSid;
00885   cCaDescr filterCaDescr;
00886   unsigned char lastCw[16];
00887   bool sync, noKey, trigger, ecmUpd;
00888   int triggerMode;
00889   int mode, count;
00890   cTimeMs lastsync, startecm, resendTime;
00891   unsigned int cryptPeriod;
00892   unsigned char parity;
00893   cMsgCache failed;
00894   //
00895   cSimpleList<cEcmInfo> ecmList;
00896   cSimpleList<cEcmPri> ecmPriList;
00897   cEcmInfo *ecm;
00898   cEcmPri *ecmPri;
00899   //
00900   int dolog;
00901   //
00902   void DeleteSys(void);
00903   void NoSync(bool clearParity);
00904   cEcmInfo *NewEcm(void);
00905   cEcmInfo *JumpEcm(void);
00906   void StopEcm(void);
00907   bool UpdateEcm(void);
00908   void EcmOk(void);
00909   void EcmFail(void);
00910   void ParseCAInfo(int sys);
00911   void AddEcmPri(cEcmInfo *n);
00912 protected:
00913   virtual void Process(cPidFilter *filter, unsigned char *data, int len);
00914 public:
00915   cEcmHandler(cCam *Cam, cDevice *Device, const char *DevId, int cwindex);
00916   virtual ~cEcmHandler();
00917   void Stop(void);
00918   void SetPrg(cPrg *Prg);
00919   void ShiftCwIndex(int cwindex);
00920   char *CurrentKeyStr(void) const;
00921   bool IsRemoveable(void);
00922   bool IsIdle(void);
00923   int Sid(void) const { return prg.sid; }
00924   int CwIndex(void) const { return cwIndex; }
00925   const char *Id(void) const { return id; }
00926   };
00927 
00928 cEcmHandler::cEcmHandler(cCam *Cam, cDevice *Device, const char *DevId, int cwindex)
00929 :cAction("ecmhandler",Device,DevId)
00930 ,failed(32,0)
00931 {
00932   cam=Cam;
00933   devId=DevId;
00934   cwIndex=cwindex;
00935   sys=0; filter=0; ecm=0; ecmPri=0; mode=-1;
00936   trigger=ecmUpd=false; triggerMode=-1;
00937   filterSource=filterTransponder=0; filterCwIndex=-1; filterSid=-1;
00938   id=bprintf("%s.%d",devId,cwindex);
00939 }
00940 
00941 cEcmHandler::~cEcmHandler()
00942 {
00943   Lock();
00944   StopEcm();
00945   DelAllFilter(); // delete filters before sys for multi-threading reasons
00946   DeleteSys();
00947   Unlock();
00948   free(id);
00949 }
00950 
00951 bool cEcmHandler::IsIdle(void)
00952 {
00953   dataMutex.Lock();
00954   int n=prg.pids.Count();
00955   dataMutex.Unlock();
00956   return n==0;
00957 }
00958 
00959 bool cEcmHandler::IsRemoveable(void)
00960 {
00961   return IsIdle() && idleTime.Elapsed()>MAX_ECM_IDLE;
00962 }
00963 
00964 void cEcmHandler::Stop(void)
00965 {
00966   dataMutex.Lock();
00967   if(!IsIdle() || prg.sid!=-1) {
00968     PRINTF(L_CORE_ECM,"%s: stop",id);
00969     prg.sid=-1;
00970     idleTime.Set();
00971     prg.pids.Clear();
00972     trigger=true;
00973     }
00974   dataMutex.Unlock();
00975   if(filter) filter->Wakeup();
00976 }
00977 
00978 void cEcmHandler::ShiftCwIndex(int cwindex)
00979 {
00980   if(cwIndex!=cwindex) {
00981     PRINTF(L_CORE_PIDS,"%s: shifting cwIndex from %d to %d",id,cwIndex,cwindex);
00982     free(id);
00983     id=bprintf("%s.%d",devId,cwindex);
00984     dataMutex.Lock();
00985     trigger=true;
00986     cwIndex=cwindex;
00987     for(cPrgPid *pid=prg.pids.First(); pid; pid=prg.pids.Next(pid))
00988       cam->SetCWIndex(pid->pid,cwIndex);
00989     dataMutex.Unlock();
00990     if(filter) filter->Wakeup();
00991     }
00992 }
00993 
00994 void cEcmHandler::SetPrg(cPrg *Prg)
00995 {
00996   dataMutex.Lock();
00997   bool wasIdle=IsIdle();
00998   if(Prg->sid!=prg.sid) {
00999     PRINTF(L_CORE_ECM,"%s: setting new SID %d",id,Prg->sid);
01000     prg.sid=Prg->sid;
01001     prg.source=Prg->source;
01002     prg.transponder=Prg->transponder;
01003     idleTime.Set();
01004     prg.pids.Clear();
01005     trigger=true;
01006     }
01007   if(Prg->HasPidCaDescr())
01008     PRINTF(L_GEN_DEBUG,"internal: pid specific caDescr not supported at this point (sid=%d)",Prg->sid);
01009   LBSTART(L_CORE_PIDS);
01010   LBPUT("%s: pids on entry",id);
01011   for(cPrgPid *pid=prg.pids.First(); pid; pid=prg.pids.Next(pid))
01012     LBPUT(" %s=%04x",TYPENAME(pid->type),pid->pid);
01013   LBEND();
01014 
01015   for(cPrgPid *pid=prg.pids.First(); pid;) {
01016     cPrgPid *npid;
01017     for(npid=Prg->pids.First(); npid; npid=Prg->pids.Next(npid)) {
01018       if(pid->pid==npid->pid) {
01019         npid->Proc(true);
01020         break;
01021         }
01022       }
01023     if(!npid) {
01024       npid=prg.pids.Next(pid);
01025       prg.pids.Del(pid);
01026       pid=npid;
01027       }
01028     else pid=prg.pids.Next(pid);
01029     }
01030   LBSTART(L_CORE_PIDS);
01031   LBPUT("%s: pids after delete",id);
01032   for(cPrgPid *pid=prg.pids.First(); pid; pid=prg.pids.Next(pid))
01033     LBPUT(" %s=%04x",TYPENAME(pid->type),pid->pid);
01034   LBEND();
01035   for(cPrgPid *npid=Prg->pids.First(); npid; npid=Prg->pids.Next(npid)) {
01036     if(!npid->Proc()) {
01037       cPrgPid *pid=new cPrgPid(npid->type,npid->pid);
01038       prg.pids.Add(pid);
01039       cam->SetCWIndex(pid->pid,cwIndex);
01040       }
01041     }
01042   LBSTART(L_CORE_PIDS);
01043   LBPUT("%s: pids after add",id);
01044   for(cPrgPid *pid=prg.pids.First(); pid; pid=prg.pids.Next(pid))
01045     LBPUT(" %s=%04x",TYPENAME(pid->type),pid->pid);
01046   LBEND();
01047   if(!IsIdle()) {
01048     if(!(prg.caDescr==Prg->caDescr)) prg.caDescr.Set(&Prg->caDescr);
01049     trigger=true;
01050     triggerMode=0;
01051     if(wasIdle) PRINTF(L_CORE_ECM,"%s: is no longer idle",id);
01052     }
01053   else {
01054     if(!wasIdle) idleTime.Set();
01055     PRINTF(L_CORE_ECM,"%s: is idle%s",id,wasIdle?"":" now");
01056     }
01057 
01058   if(!filter) {
01059     filter=NewFilter(IDLE_SLEEP);
01060     if(!filter) PRINTF(L_GEN_ERROR,"failed to open ECM filter in handler %s",id);
01061     }
01062   dataMutex.Unlock();
01063   if(filter) filter->Wakeup();
01064 }
01065 
01066 void cEcmHandler::Process(cPidFilter *filter, unsigned char *data, int len)
01067 {
01068   dataMutex.Lock();
01069   if(trigger) {
01070     PRINTF(L_CORE_ECM,"%s: triggered SID %d/%d idx %d/%d mode %d/%d %s",
01071       id,filterSid,prg.sid,filterCwIndex,cwIndex,mode,triggerMode,(mode==3 && sync)?"sync":"-");
01072     trigger=false;
01073     if(filterSid!=prg.sid) {
01074       filterSid=prg.sid;
01075       filterSource=prg.source;
01076       filterTransponder=prg.transponder;
01077       filterCwIndex=cwIndex;
01078       noKey=true; mode=0;
01079       }
01080     else {
01081       if(filterCwIndex!=cwIndex) {
01082         filterCwIndex=cwIndex;
01083         if(mode==3 && sync)
01084           cam->WriteCW(filterCwIndex,lastCw,true);
01085         }
01086       if(mode<triggerMode) mode=triggerMode;
01087       }
01088     if(!(prg.caDescr==filterCaDescr)) {
01089       filterCaDescr.Set(&prg.caDescr);
01090       ecmUpd=true;
01091 //XXX
01092 PRINTF(L_CORE_ECM,"%s: new caDescr: %s",id,*filterCaDescr.ToString());
01093       }
01094     triggerMode=-1;
01095     }
01096   dataMutex.Unlock();
01097 
01098   switch(mode) {
01099     case -1:
01100       filter->SetIdleTime(IDLE_SLEEP);
01101       break;
01102 
01103     case 0:
01104       StopEcm();
01105       if(IsIdle()) { mode=-1; break; }
01106 
01107       dolog=LOG_COUNT;
01108       NewEcm();
01109       filter->SetIdleTime(IDLE_GETCA);
01110       startecm.Set();
01111       mode=1;
01112       break;
01113 
01114     case 1:
01115       if(!ecm && !JumpEcm()) {
01116         if(startecm.Elapsed()>IDLE_GETCA_SLOW) {
01117           if(IsIdle()) { mode=0; break; }
01118           PRINTF(L_CORE_ECM,"%s: no encryption system found",id);
01119           filter->SetIdleTime(IDLE_GETCA_SLOW/4);
01120           startecm.Set();
01121           }
01122         break;
01123         }
01124       mode=4;
01125       // fall through
01126 
01127     case 4:
01128     case 5:
01129       NoSync(mode==4);
01130       failed.Clear();
01131       filter->SetIdleTime(IDLE_NO_SYNC/2);
01132       lastsync.Set();
01133       cryptPeriod=20*1000;
01134       mode=2;
01135       // fall through
01136           
01137     case 2:
01138       if(sys->NeedsData()) {
01139         if(!UpdateEcm()) {
01140           if(lastsync.Elapsed()<ECM_DATA_TIME) break;
01141           PRINTF(L_CORE_ECM,"%s: no ecm extra data update (waited %d ms)",id,(int)lastsync.Elapsed());
01142           }
01143         if(lastsync.Elapsed()>IDLE_NO_SYNC/4 && dolog)
01144           PRINTF(L_CORE_ECM,"%s: ecm extra data update took %d ms",id,(int)lastsync.Elapsed());
01145         }
01146       filter->SetIdleTime(IDLE_NO_SYNC);
01147       mode=3;
01148       // fall through
01149 
01150     case 3:
01151       {
01152       bool resend=false, cwok=false;
01153       if(resendTime.TimedOut()) {
01154         resend=sync; resendTime.Set(8*24*60*60*1000);
01155         }
01156 
01157       if(startecm.Elapsed()<3*60*1000) cam->DumpAV7110();
01158 
01159       if(data && len>0) {
01160         HEXDUMP(L_HEX_ECM,data,len,"ECM sys 0x%04x id 0x%02x pid 0x%04x",ecm->caId,ecm->provId,filter->Pid());
01161         if(SCT_LEN(data)==len) {
01162           if(ecm->rewriter) {
01163             ecm->rewriter->Rewrite(data,len);
01164             HEXDUMP(L_HEX_ECM,data,len,"rewritten to");
01165             }
01166           LDUMP(L_CORE_ECMPROC,data,16,"%s: ECM",id);
01167           int n;
01168           if(!(n=sys->CheckECM(ecm,data,sync))) {
01169             if(resend || parity!=(data[0]&1)) {
01170               int ecmid;
01171               cTimeMs procTime;
01172               cwok=(ecmid=failed.Get(data,len,0))>=0 && sys->ProcessECM(ecm,data);
01173               n=(ecmid>0)?failed.Cache(ecmid,cwok,0):99;
01174               sys->CheckECMResult(ecm,data,cwok);
01175               if(cwok) {
01176                 parity=data[0]&1;
01177                 }
01178               else {
01179                 if(procTime.Elapsed()>6000) {
01180                   PRINTF(L_CORE_ECM,"%s: filter flush (elapsed %d)",id,(int)procTime.Elapsed());
01181                   filter->Flush();
01182                   }
01183                 if(n>=2) { count++; if(n==2) count++; }
01184                 parity=0xFF;
01185                 if(sync && lastsync.Elapsed()>cryptPeriod*2) {
01186                   PRINTF(L_CORE_ECM,"%s: lost sync (period %d, elapsed %d)",id,cryptPeriod,(int)lastsync.Elapsed());
01187                   NoSync(true);
01188                   }
01189                 }
01190               PRINTF(L_CORE_ECMPROC,"%s: (%s) cwok=%d ecmid=%d n=%d sync=%d parity=%d count=%d ELA=%d",
01191                     id,sys->Name(),cwok,ecmid,n,sync,parity,count,(int)procTime.Elapsed());
01192               }
01193             }
01194           else {
01195             PRINTF(L_CORE_ECMPROC,"%s: check result %d",id,n);
01196             switch(n) {
01197               case 1: NoSync(true); break;
01198               case 2: count++; break;
01199               case 3: break;
01200               }
01201             }
01202           }
01203         else {
01204           PRINTF(L_CORE_ECM,"%s: incomplete section %d != %d",id,len,SCT_LEN(data));
01205           count++;
01206           }
01207         }
01208       else if(sys->Constant()) {
01209         if(sys->ProcessECM(ecm,NULL)) {
01210           cwok=true;
01211           if(sync) filter->SetIdleTime(IDLE_SYNC*10); 
01212           }
01213         else count++;
01214         }
01215       else count++;
01216 
01217       if(cwok) {
01218         dolog=LOG_COUNT; sys->DoLog(true);
01219         cam->WriteCW(filterCwIndex,sys->CW(),resend || !sync);
01220         memcpy(lastCw,sys->CW(),sizeof(lastCw));
01221         noKey=false; count=0;
01222         UpdateEcm(); EcmOk();
01223         if(!sync) {
01224           sync=true;
01225           filter->SetIdleTime(IDLE_SYNC);
01226           PRINTF(L_CORE_ECM,"%s: correct key found",id);
01227           if(!cam->IsSoftCSA(filterCwIndex==0))
01228             resendTime.Set(CW_REPEAT_TIME);
01229           }
01230         else if(!resend)
01231           cryptPeriod=max(5000,min(60000,(int)lastsync.Elapsed()));
01232         lastsync.Set();
01233         }
01234 
01235       if(!sync && !trigger) {
01236         if(count>=sys->MaxEcmTry()) {
01237           EcmFail(); JumpEcm();
01238           mode=4;
01239           if(!ecm) {
01240             JumpEcm();
01241             if(!ecm) { // this should not happen!
01242               PRINTF(L_GEN_DEBUG,"internal: handler %s, empty ecm list in sync loop",id);
01243               mode=0; break;
01244               }
01245             // if we looped through all systems, we wait until the next parity
01246             // change before we try again.
01247             if(dolog!=LOG_COUNT && data) { parity=data[0]&1; mode=5; }
01248             if(dolog && !--dolog) {
01249               sys->DoLog(false);
01250               PRINTF(L_CORE_ECM,"%s: stopping message log until valid key is found",id);
01251               }
01252             }
01253           break;
01254           }
01255         }
01256         
01257       if(IsIdle() && idleTime.Elapsed()>MAX_ECM_HOLD) {
01258         PRINTF(L_CORE_ECM,"%s: hold timeout expired",id);
01259         mode=0;
01260         }
01261 
01262       break;
01263       }
01264     }
01265 }
01266 
01267 void cEcmHandler::NoSync(bool clearParity)
01268 {
01269   if(clearParity) parity=0xFF;
01270   count=0; sync=false;
01271 }
01272 
01273 void cEcmHandler::DeleteSys(void)
01274 {
01275   delete sys; sys=0;
01276 }
01277 
01278 char *cEcmHandler::CurrentKeyStr(void) const
01279 {
01280   if(noKey || !sys) return 0;
01281   return strdup(sys->CurrentKeyStr());
01282 }
01283 
01284 cEcmInfo *cEcmHandler::NewEcm(void)
01285 {
01286   ecmcache.GetCached(&ecmList,filterSid,filterSource,filterTransponder);
01287   ecmPriList.Clear();
01288   for(cEcmInfo *n=ecmList.First(); n; n=ecmList.Next(n)) AddEcmPri(n);
01289   ecm=0; ecmPri=0;
01290   return JumpEcm();
01291 }
01292 
01293 void cEcmHandler::AddEcmPri(cEcmInfo *n)
01294 {
01295   int ident, pri=0;
01296   while(1) {
01297     if(overrides.Ignore(n->source,n->transponder,n->caId)) break;
01298     if(!n->Cached()) ident=cSystems::FindIdentBySysId(n->caId,!cam->IsSoftCSA(filterCwIndex==0),pri);
01299     else ident=(pri==0) ? cSystems::FindIdentBySysName(n->caId,!cam->IsSoftCSA(filterCwIndex==0),n->name,pri) : 0;
01300     if(ident<=0) break;
01301 
01302     cEcmPri *ep=new cEcmPri;
01303     if(ep) {
01304       ep->ecm=n; 
01305       ep->pri=pri;
01306       ep->sysIdent=ident;
01307       if(n->Cached() && (!ScSetup.LocalPriority || pri!=-15)) ep->pri+=20;
01308       ep->pri=ep->pri*100 + overrides.GetEcmPrio(n->source,n->transponder,n->caId,n->provId);
01309 
01310       // no double entries in ecmPriList
01311       for(cEcmPri *epp=ecmPriList.First(); epp; epp=ecmPriList.Next(epp))
01312         if(epp->ecm==ep->ecm && epp->sysIdent==ep->sysIdent) {
01313           delete ep; ep=0;
01314           break;
01315           }
01316       }
01317     if(ep) {
01318       // keep ecmPriList sorted
01319       cEcmPri *eppp, *epp=ecmPriList.First();
01320       if(!epp || epp->pri<ep->pri)
01321         ecmPriList.Ins(ep);
01322       else {
01323         do {
01324           eppp=ecmPriList.Next(epp);
01325           if(!eppp || eppp->pri<ep->pri) {
01326             ecmPriList.Add(ep,epp);
01327             break;
01328             }
01329           } while((epp=eppp));
01330         }
01331       }
01332     }
01333 }
01334 
01335 void cEcmHandler::StopEcm(void)
01336 {
01337   filter->Stop(); filter->Flush();
01338   if(ecm) cam->LogEcmStatus(ecm,false);
01339   DeleteSys();
01340 }
01341 
01342 bool cEcmHandler::UpdateEcm(void)
01343 {
01344   if(ecmUpd) {
01345     bool log=dolog;
01346     dolog=(sys && sys->NeedsData() && ecm->Data()==0);
01347     if(dolog) PRINTF(L_CORE_ECM,"%s: try to update ecm extra data",id);
01348     ParseCAInfo(ecm->caId);
01349     dolog=log;
01350     }
01351   return ecm->Data()!=0;
01352 }
01353 
01354 cEcmInfo *cEcmHandler::JumpEcm(void)
01355 {
01356   noKey=true;
01357   if(!ecmPri) {
01358     ParseCAInfo(0xFFFF); // all systems
01359     ecmPri=ecmPriList.First();
01360     }
01361   else ecmPri=ecmPriList.Next(ecmPri);
01362   if(ecmPri) {
01363     if(ecmPri->ecm!=ecm) {
01364       StopEcm();
01365       ecm=ecmPri->ecm;
01366       filter->Start(ecm->ecm_pid,ecm->ecm_table,0xfe);
01367       cam->LogEcmStatus(ecm,true);
01368       }
01369     else {
01370       DeleteSys();
01371       filter->Flush();
01372       }
01373     sys=cSystems::FindBySysIdent(ecmPri->sysIdent);
01374     if(!sys) {
01375       if(dolog) PRINTF(L_GEN_DEBUG,"internal: handler %s, no system found for ident %04x (caid %04x pri %d)",id,ecmPri->sysIdent,ecmPri->ecm->caId,ecmPri->pri);
01376       return JumpEcm();
01377       }
01378     sys->DoLog(dolog!=0);
01379     sys->SetOwner(cam);
01380     failed.SetMaxFail(sys->MaxEcmTry());
01381 
01382     if(dolog) PRINTF(L_CORE_ECM,"%s: try system %s (%04x) id %04x with ecm %x%s (pri=%d)",
01383                      id,sys->Name(),ecm->caId,ecm->provId,ecm->ecm_pid,ecm->Cached()?" (cached)":"",sys->Pri());
01384     }
01385   else {
01386     StopEcm();
01387     ecm=0;
01388     }
01389   return ecm;
01390 }
01391 
01392 void cEcmHandler::EcmOk(void)
01393 {
01394   ecm->SetName(sys->Name());
01395   ecm->Fail(false);
01396   ecmcache.New(ecm);
01397   cEcmInfo *e=ecmList.First();
01398   while(e) {
01399     if(e->Cached() && e->Failed()) ecmcache.Delete(e);
01400     e=ecmList.Next(e);
01401     }
01402 }
01403 
01404 void cEcmHandler::EcmFail(void)
01405 {
01406   ecm->Fail(true);
01407 }
01408 
01409 void cEcmHandler::ParseCAInfo(int SysId)
01410 {
01411   ecmUpd=false;
01412   int len;
01413   const unsigned char *buff=filterCaDescr.Get(len);
01414   if(buff && len>0) {
01415     if(dolog) PRINTF(L_CORE_ECM,"%s: CA descriptors for SID %d (len=%d)",id,filterSid,len);
01416     HEXDUMP(L_HEX_PMT,buff,len,"PMT");
01417     for(int index=0; index<len; index+=buff[index+1]+2) {
01418       if(buff[index]==0x09) {
01419         int sysId=WORD(buff,index+2,0xFFFF);
01420         if(SysId!=0xFFFF && sysId!=SysId) continue;
01421         if(dolog) LDUMP(L_CORE_ECM,&buff[index+2],buff[index+1],"%s: descriptor",id);
01422         if(overrides.Ignore(filterSource,filterTransponder,sysId)) {
01423           if(dolog) PRINTF(L_CORE_ECM,"%s: system %04x ignored",id,sysId);
01424           continue;
01425           }
01426         int sysPri=0;
01427         cSystem *sys;
01428         while((sys=cSystems::FindBySysId(sysId,!cam->IsSoftCSA(filterCwIndex==0),sysPri))) {
01429           sysPri=sys->Pri();
01430           cSimpleList<cEcmInfo> ecms;
01431           sys->ParseCADescriptor(&ecms,sysId,filterSource,&buff[index+2],buff[index+1]);
01432           delete sys;
01433           if(ecms.Count()) {
01434             cEcmInfo *n;
01435             while((n=ecms.First())) {
01436               ecms.Del(n,false);
01437               n->SetSource(filterSid,filterSource,filterTransponder);
01438               n->SetDvb(cam->Adapter(),cam->Frontend());
01439               n->AddCaDescr(&buff[index],buff[index+1]+2);
01440               overrides.UpdateEcm(n,dolog);
01441               LBSTARTF(L_CORE_ECM);
01442               if(dolog) LBPUT("%s: found %04x(%04x) (%s) id %04x with ecm %x/%x ",id,n->caId,n->emmCaId,n->name,n->provId,n->ecm_pid,n->ecm_table);
01443               cEcmInfo *e=ecmList.First();
01444               while(e) {
01445                 if(e->ecm_pid==n->ecm_pid) {
01446                   if(e->caId==n->caId && e->provId==n->provId) {
01447                     if(e->AddCaDescr(n) && dolog) LBPUT("(updated) ");
01448                     if(dolog) LBPUT("(already present)");
01449                     AddEcmPri(e);
01450                     delete n; n=0;
01451                     break;
01452                     }
01453                   else {
01454                     e->Fail(true);
01455                     if(dolog) LBPUT("(dup) ");
01456                     }
01457                   }
01458                 e=ecmList.Next(e);
01459                 }
01460               if(n) {
01461                 if(dolog) LBPUT("(new)");
01462                 ecmList.Add(n);
01463                 AddEcmPri(n);
01464                 }
01465               LBEND();
01466               }
01467             break;
01468             }
01469           }
01470         if(sysPri==0 && dolog) PRINTF(L_CORE_ECM,"%s: no module available for system %04x",id,sysId);
01471         }
01472       }
01473     }
01474 
01475   for(cEcmPri *ep=ecmPriList.First(); ep; ep=ecmPriList.Next(ep))
01476     PRINTF(L_CORE_ECMPROC,"%s: ecmPriList pri=%d ident=%04x caid=%04x pid=%04x",id,ep->pri,ep->sysIdent,ep->ecm->caId,ep->ecm->ecm_pid);
01477   PRINTF(L_CORE_ECMPROC,"%s: ecmPri list end",id);
01478 }
01479 
01480 // --- cChannelCaids -----------------------------------------------------------
01481 
01482 #ifndef SASC
01483 
01484 class cChannelCaids : public cSimpleItem {
01485 private:
01486   int prg, source, transponder;
01487   int numcaids;
01488   caid_t caids[MAX_CI_SLOT_CAIDS+1];
01489 public:
01490   cChannelCaids(cChannel *channel);
01491   bool IsChannel(cChannel *channel);
01492   void Sort(void);
01493   void Del(caid_t caid);
01494   bool HasCaid(caid_t caid);
01495   bool Same(cChannelCaids *ch, bool full);
01496   void HistAdd(unsigned short *hist);
01497   void Dump(const char *devId);
01498   const caid_t *Caids(void) { caids[numcaids]=0; return caids; }
01499   int NumCaids(void) { return numcaids; }
01500   int Source(void) const { return source; }
01501   int Transponder(void) const { return transponder; }
01502   };
01503 
01504 cChannelCaids::cChannelCaids(cChannel *channel)
01505 {
01506   prg=channel->Sid(); source=channel->Source(); transponder=channel->Transponder();
01507   numcaids=0;
01508   for(const caid_t *ids=channel->Caids(); *ids; ids++)
01509     if(numcaids<MAX_CI_SLOT_CAIDS) caids[numcaids++]=*ids;
01510   Sort();
01511 }
01512 
01513 bool cChannelCaids::IsChannel(cChannel *channel)
01514 {
01515   return prg==channel->Sid() && source==channel->Source() && transponder==channel->Transponder();
01516 }
01517 
01518 void cChannelCaids::Sort(void)
01519 {
01520   caid_t tmp[MAX_CI_SLOT_CAIDS];
01521   int c=0xFFFF;
01522   for(int i=0; i<numcaids; i++) {
01523     int d=0;
01524     for(int j=0; j<numcaids; j++) if(caids[j]>d && caids[j]<c) d=caids[j];
01525     tmp[i]=d; c=d;
01526     }
01527   memcpy(caids,tmp,sizeof(caids));
01528 }
01529 
01530 void cChannelCaids::Del(caid_t caid)
01531 {
01532   for(int i=0; i<numcaids; i++)
01533     if(caids[i]==caid) {
01534       numcaids--; caids[i]=caids[numcaids];
01535       if(numcaids>0) Sort();
01536       caids[numcaids]=0;
01537       break;
01538       }
01539 }
01540 
01541 bool cChannelCaids::HasCaid(caid_t caid)
01542 {
01543   for(int i=0; i<numcaids; i++) if(caids[i]==caid) return true;
01544   return false;
01545 }
01546 
01547 bool cChannelCaids::Same(cChannelCaids *ch, bool full)
01548 {
01549   if(full && (source!=ch->source || transponder!=ch->transponder)) return false;
01550   if(numcaids!=ch->numcaids) return false;
01551   return memcmp(caids,ch->caids,numcaids*sizeof(caid_t))==0;
01552 }
01553 
01554 void cChannelCaids::HistAdd(unsigned short *hist)
01555 {
01556   for(int i=numcaids-1; i>=0; i--) hist[caids[i]]++;
01557 }
01558 
01559 void cChannelCaids::Dump(const char *devId)
01560 {
01561   LBSTART(L_CORE_CAIDS);
01562   LBPUT("%s: channel %d/%x/%x",devId,prg,source,transponder);
01563   for(const caid_t *ids=Caids(); *ids; ids++) LBPUT(" %04x",*ids);
01564   LBEND();
01565 }
01566 
01567 // --- cChannelList ------------------------------------------------------------
01568 
01569 class cChannelList : public cSimpleList<cChannelCaids> {
01570 private:
01571   const char *devId;
01572 public:
01573   cChannelList(const char *DevId);
01574   void Unique(bool full);
01575   void CheckIgnore(void);
01576   int Histo(void);
01577   void Purge(int caid, bool fullch);
01578   };
01579 
01580 cChannelList::cChannelList(const char *DevId)
01581 {
01582   devId=DevId;
01583 }
01584 
01585 void cChannelList::CheckIgnore(void)
01586 {
01587   char *cache=MALLOC(char,0x10000);
01588   if(!cache) return;
01589   memset(cache,0,sizeof(char)*0x10000);
01590   int cTotal=0, cHits=0;
01591   for(cChannelCaids *ch=First(); ch; ch=Next(ch)) {
01592     const caid_t *ids=ch->Caids();
01593     while(*ids) {
01594       int pri=0;
01595       if(overrides.Ignore(ch->Source(),ch->Transponder(),*ids))
01596         ch->Del(*ids);
01597       else {
01598         char c=cache[*ids];
01599         if(c==0) cache[*ids]=c=(cSystems::FindIdentBySysId(*ids,false,pri) ? 1 : -1);
01600         else cHits++;
01601         cTotal++;
01602         if(c<0) {
01603           for(cChannelCaids *ch2=Next(ch); ch2; ch2=Next(ch2)) ch2->Del(*ids);
01604           ch->Del(*ids);
01605           }
01606         else ids++;
01607         }
01608       }
01609     }
01610   free(cache);
01611   PRINTF(L_CORE_CAIDS,"%s: after check",devId);
01612   for(cChannelCaids *ch=First(); ch; ch=Next(ch)) ch->Dump(devId);
01613   PRINTF(L_CORE_CAIDS,"%s: check cache usage: %d requests, %d hits, %d%% hits",devId,cTotal,cHits,(cTotal>0)?(cHits*100/cTotal):0);
01614 }
01615 
01616 void cChannelList::Unique(bool full)
01617 {
01618   for(cChannelCaids *ch1=First(); ch1; ch1=Next(ch1)) {
01619     for(cChannelCaids *ch2=Next(ch1); ch2;) {
01620       if(ch1->Same(ch2,full) || ch2->NumCaids()<1) {
01621         cChannelCaids *t=Next(ch2);
01622         Del(ch2);
01623         ch2=t;
01624         }
01625       else ch2=Next(ch2);
01626       }
01627     }
01628   if(Count()==1 && First() && First()->NumCaids()<1) Del(First());
01629   PRINTF(L_CORE_CAIDS,"%s: after unique (%d)",devId,full);
01630   for(cChannelCaids *ch=First(); ch; ch=Next(ch)) ch->Dump(devId);
01631 }
01632 
01633 int cChannelList::Histo(void)
01634 {
01635   int h=-1;
01636   unsigned short *hist=MALLOC(unsigned short,0x10000);
01637   if(hist) {
01638     memset(hist,0,sizeof(unsigned short)*0x10000);
01639     for(cChannelCaids *ch=First(); ch; ch=Next(ch)) ch->HistAdd(hist);
01640     int c=0;
01641     for(int i=0; i<0x10000; i++)
01642       if(hist[i]>c) { h=i; c=hist[i]; }
01643     free(hist);
01644     }
01645   else PRINTF(L_GEN_ERROR,"malloc failed in cChannelList::Histo");
01646   return h;
01647 }
01648 
01649 void cChannelList::Purge(int caid, bool fullch)
01650 {
01651   for(cChannelCaids *ch=First(); ch;) {
01652     if(!fullch) ch->Del(caid);
01653     if(ch->NumCaids()<=0 || (fullch && ch->HasCaid(caid))) {
01654       cChannelCaids *t=Next(ch);
01655       Del(ch);
01656       ch=t;
01657       }
01658     else ch=Next(ch);
01659     }
01660   if(Count()>0) {
01661     PRINTF(L_CORE_CAIDS,"%s: still left",devId);
01662     for(cChannelCaids *ch=First(); ch; ch=Next(ch)) ch->Dump(devId);
01663     }
01664 }
01665 
01666 // -- cCiFrame -----------------------------------------------------------------
01667 
01668 #define LEN_OFF 2
01669 
01670 cCiFrame::cCiFrame(void)
01671 {
01672   rb=0; mem=0; len=alen=glen=0;
01673 }
01674 
01675 cCiFrame::~cCiFrame()
01676 {
01677   free(mem);
01678 }
01679 
01680 unsigned char *cCiFrame::GetBuff(int l)
01681 {
01682   if(!mem || l>alen) {
01683     free(mem); mem=0; alen=0;
01684     mem=MALLOC(unsigned char,l+LEN_OFF);
01685     if(mem) alen=l;
01686     }
01687   len=l;
01688   if(!mem) {
01689     PRINTF(L_GEN_DEBUG,"internal: ci-frame alloc failed");
01690     return 0;
01691     }
01692   return mem+LEN_OFF;
01693 }
01694 
01695 void cCiFrame::Put(void)
01696 {
01697   if(rb && mem) {
01698     *((short *)mem)=len;
01699     rb->Put(mem,len+LEN_OFF);
01700     }
01701 }
01702 
01703 unsigned char *cCiFrame::Get(int &l)
01704 {
01705   if(rb) {
01706     int c;
01707     unsigned char *data=rb->Get(c);
01708     if(data) {
01709       if(c>LEN_OFF) {
01710         int s=*((short *)data);
01711         if(c>=s+LEN_OFF) {
01712           l=glen=s;
01713           return data+LEN_OFF;
01714           }
01715         }
01716       LDUMP(L_GEN_DEBUG,data,c,"internal: ci rb frame sync got=%d avail=%d -",c,rb->Available());
01717       rb->Clear();
01718       }
01719     }
01720   return 0;
01721 }
01722 
01723 int cCiFrame::Avail(void)
01724 {
01725   return rb ? rb->Available() : 0;
01726 }
01727 
01728 void cCiFrame::Del(void)
01729 {
01730   if(rb && glen) {
01731     rb->Del(glen+LEN_OFF);
01732     glen=0;
01733     }
01734 }
01735 
01736 // -- cScCamSlot ---------------------------------------------------------------
01737 
01738 #define TDPU_SIZE_INDICATOR 0x80
01739 
01740 #define CAID_TIME       300000 // time between caid scans
01741 #define TRIGGER_TIME     10000 // min. time between caid scan trigger
01742 #define SLOT_CAID_CHECK  10000
01743 #define SLOT_RESET_TIME    600
01744 
01745 class cScCamSlot : public cCamSlot {
01746 private:
01747   cCam *cam;
01748   unsigned short caids[MAX_CI_SLOT_CAIDS+1];
01749   int slot, version;
01750   const char *devId;
01751   cTimeMs checkTimer;
01752   bool reset, doReply;
01753   cTimeMs resetTimer;
01754   eModuleStatus lastStatus;
01755   cRingBufferLinear rb;
01756   cCiFrame frame;
01757   //
01758   int GetLength(const unsigned char * &data);
01759   int LengthSize(int n);
01760   void SetSize(int n, unsigned char * &p);
01761   void CaInfo(int tcid, int cid);
01762   bool Check(void);
01763 public:
01764   cScCamSlot(cCam *Cam, const char *DevId, int Slot);
01765   void Process(const unsigned char *data, int len);
01766   eModuleStatus Status(void);
01767   bool Reset(bool log=true);
01768   cCiFrame *Frame(void) { return &frame; }
01769   };
01770 
01771 cScCamSlot::cScCamSlot(cCam *Cam, const char *DevId, int Slot)
01772 :cCamSlot(Cam)
01773 ,checkTimer(-SLOT_CAID_CHECK-1000)
01774 ,rb(KILOBYTE(4),5+LEN_OFF,false,"SC-CI slot answer")
01775 {
01776   cam=Cam; devId=DevId; slot=Slot;
01777   version=0; caids[0]=0; doReply=false; lastStatus=msReset;
01778   frame.SetRb(&rb);
01779   Reset(false);
01780 }
01781 
01782 eModuleStatus cScCamSlot::Status(void)
01783 {
01784   eModuleStatus status;
01785   if(reset) {
01786     status=msReset;
01787     if(resetTimer.TimedOut()) reset=false;
01788     }
01789   else if(caids[0]) status=msReady;
01790   else {
01791     status=msPresent; //msNone;
01792     Check();
01793     }
01794   if(status!=lastStatus) {
01795     static const char *stext[] = { "none","reset","present","ready" };
01796     PRINTF(L_CORE_CI,"%s.%d: status '%s'",devId,slot,stext[status]);
01797     lastStatus=status;
01798     }
01799   return status;
01800 }
01801 
01802 bool cScCamSlot::Reset(bool log)
01803 {
01804   reset=true; resetTimer.Set(SLOT_RESET_TIME);
01805   rb.Clear();
01806   if(log) PRINTF(L_CORE_CI,"%s.%d: reset",devId,slot);
01807   return true;
01808 }
01809 
01810 bool cScCamSlot::Check(void)
01811 {
01812   bool res=false;
01813   bool dr=cam->IsSoftCSA(false) || ScSetup.ConcurrentFF>0;
01814   if(dr!=doReply && !IsDecrypting()) {
01815     PRINTF(L_CORE_CI,"%s.%d: doReply changed, reset triggered",devId,slot);
01816     Reset(false);
01817     doReply=dr;
01818     }
01819   if(checkTimer.TimedOut()) {
01820     if(version!=cam->GetCaids(slot,0,0)) {
01821       version=cam->GetCaids(slot,caids,MAX_CI_SLOT_CAIDS);
01822       PRINTF(L_CORE_CI,"%s.%d: now using CAIDs version %d",devId,slot,version);
01823       res=true;
01824       }
01825     checkTimer.Set(SLOT_CAID_CHECK);
01826     }
01827   return res;
01828 }
01829 
01830 int cScCamSlot::GetLength(const unsigned char * &data)
01831 {
01832   int len=*data++;
01833   if(len&TDPU_SIZE_INDICATOR) {
01834     int i;
01835     for(i=len&~TDPU_SIZE_INDICATOR, len=0; i>0; i--) len=(len<<8) + *data++;
01836     }
01837   return len;
01838 }
01839 
01840 int cScCamSlot::LengthSize(int n)
01841 {
01842   return n<TDPU_SIZE_INDICATOR?1:3;
01843 }
01844 
01845 void cScCamSlot::SetSize(int n, unsigned char * &p)
01846 {
01847   if(n<TDPU_SIZE_INDICATOR) *p++=n;
01848   else { *p++=2|TDPU_SIZE_INDICATOR; *p++=n>>8; *p++=n&0xFF; }
01849 }
01850 
01851 void cScCamSlot::CaInfo(int tcid, int cid)
01852 {
01853   int cn=0;
01854   for(int i=0; caids[i]; i++) cn+=2;
01855   int n=cn+8+LengthSize(cn);
01856   unsigned char *p;
01857   if(!(p=frame.GetBuff(n+1+LengthSize(n)))) return;
01858   *p++=0xa0;
01859   SetSize(n,p);
01860   *p++=tcid;
01861   *p++=0x90;
01862   *p++=0x02; *p++=cid>>8; *p++=cid&0xff;
01863   *p++=0x9f; *p++=0x80;   *p++=0x31; // AOT_CA_INFO
01864   SetSize(cn,p);
01865   for(int i=0; caids[i]; i++) { *p++=caids[i]>>8; *p++=caids[i]&0xff; }
01866   frame.Put();
01867   PRINTF(L_CORE_CI,"%s.%d sending CA info",devId,slot);
01868 }
01869 
01870 void cScCamSlot::Process(const unsigned char *data, int len)
01871 {
01872   const unsigned char *save=data;
01873   data+=3;
01874   int dlen=GetLength(data);
01875   if(dlen>len-(data-save)) {
01876     PRINTF(L_CORE_CI,"%s.%d TDPU length exceeds data length",devId,slot);
01877     dlen=len-(data-save);
01878     }
01879   int tcid=data[0];
01880 
01881   if(Check()) CaInfo(tcid,0x01);
01882 
01883   if(dlen<8 || data[1]!=0x90) return;
01884   int cid=(data[3]<<8)+data[4];
01885   int tag=(data[5]<<16)+(data[6]<<8)+data[7];
01886   data+=8;
01887   dlen=GetLength(data);
01888   if(dlen>len-(data-save)) {
01889     PRINTF(L_CORE_CI,"%s.%d tag length exceeds data length",devId,slot);
01890     dlen=len-(data-save);
01891     }
01892   switch(tag) {
01893     case 0x9f8030: // AOT_CA_INFO_ENQ
01894       CaInfo(tcid,cid);
01895       break;
01896 
01897     case 0x9f8032: // AOT_CA_PMT
01898       if(dlen>=6) {
01899         int ca_lm=data[0];
01900         int ci_cmd=-1;
01901         cPrg *prg=new cPrg((data[1]<<8)+data[2],ca_lm==5);
01902         int ilen=(data[4]<<8)+data[5];
01903         LBSTARTF(L_CORE_CI);
01904         LBPUT("%s.%d CA_PMT decoding len=%x lm=%x prg=%d len=%x",devId,slot,dlen,ca_lm,(data[1]<<8)+data[2],ilen);
01905         data+=6; dlen-=6;
01906         LBPUT("/%x",dlen);
01907         if(ilen>0 && dlen>=ilen) {
01908           ci_cmd=data[0];
01909           if(ilen>1)
01910             prg->caDescr.Set(&data[1],ilen-1);
01911           LBPUT(" ci_cmd(G)=%02x",ci_cmd);
01912           }
01913         data+=ilen; dlen-=ilen;
01914         while(dlen>=5) {
01915           cPrgPid *pid=new cPrgPid(data[0],(data[1]<<8)+data[2]);
01916           prg->pids.Add(pid);
01917           ilen=(data[3]<<8)+data[4];
01918           LBPUT(" pid=%d,%x len=%x",data[0],(data[1]<<8)+data[2],ilen);
01919           data+=5; dlen-=5;
01920           LBPUT("/%x",dlen);
01921           if(ilen>0 && dlen>=ilen) {
01922             ci_cmd=data[0];
01923             if(ilen>1) {
01924               pid->caDescr.Set(&data[1],ilen-1);
01925               prg->SetPidCaDescr(true);
01926               }
01927             LBPUT(" ci_cmd(S)=%x",ci_cmd);
01928             }
01929           data+=ilen; dlen-=ilen;
01930           }
01931         LBEND();
01932         PRINTF(L_CORE_CI,"%s.%d got CA pmt ciCmd=%d caLm=%d",devId,slot,ci_cmd,ca_lm);
01933         if(doReply && (ci_cmd==0x03 || (ci_cmd==0x01 && ca_lm==0x03))) {
01934           unsigned char *b;
01935           if((b=frame.GetBuff(4+11))) {
01936             b[0]=0xa0; b[2]=tcid;
01937             b[3]=0x90;
01938             b[4]=0x02; b[5]=cid<<8; b[6]=cid&0xff;
01939             b[7]=0x9f; b[8]=0x80; b[9]=0x33; // AOT_CA_PMT_REPLY
01940             b[11]=prg->sid<<8;
01941             b[12]=prg->sid&0xff;
01942             b[13]=0x00;
01943             b[14]=0x81;         // CA_ENABLE
01944             b[10]=4; b[1]=4+9;
01945             frame.Put();
01946             PRINTF(L_CORE_CI,"%s.%d answer to query",devId,slot);
01947             }
01948           }
01949         if(prg->sid!=0) {
01950           if(ci_cmd==0x04 || (ci_cmd==-1 && ca_lm==0x03)) {
01951             PRINTF(L_CORE_CI,"%s.%d stop decrypt",devId,slot);
01952             cam->Stop();
01953             }
01954           if(ci_cmd==0x01 || (ci_cmd==-1 && (ca_lm==0x04 || ca_lm==0x05))) {
01955             PRINTF(L_CORE_CI,"%s.%d set CAM decrypt (prg %d)",devId,slot,prg->sid);
01956             cam->AddPrg(prg);
01957             }
01958           }
01959         delete prg;
01960         }
01961       break;
01962     }
01963 }
01964 
01965 #endif //!SASC
01966 
01967 // -- cCams --------------------------------------------------------------------
01968 
01969 class cCams : public cSimpleList<cCam> {
01970 public:
01971   cMutex listMutex;
01972   //
01973   void Register(cCam *cam);
01974   void Unregister(cCam *cam);
01975   };
01976 
01977 static cCams cams;
01978 
01979 void cCams::Register(cCam *cam)
01980 {
01981   cMutexLock lock(&listMutex);
01982   Add(cam);
01983 }
01984 
01985 void cCams::Unregister(cCam *cam)
01986 {
01987   cMutexLock lock(&listMutex);
01988   Del(cam,false);
01989 }
01990 
01991 // -- cGlobal ------------------------------------------------------------------
01992 
01993 char *cGlobal::CurrKeyStr(int camindex, int num, const char **id)
01994 {
01995   cMutexLock lock(&cams.listMutex);
01996   char *str=0;
01997   cCam *c;
01998   for(c=cams.First(); c && camindex>0; c=cams.Next(c), camindex--);
01999   if(c) {
02000     str=c->CurrentKeyStr(num,id);
02001     if(!str && num==0) str=strdup(tr("(none)"));
02002     }
02003   return str;
02004 }
02005 
02006 void cGlobal::HouseKeeping(void)
02007 {
02008   cMutexLock lock(&cams.listMutex);
02009   for(cCam *c=cams.First(); c; c=cams.Next(c)) c->HouseKeeping();
02010 }
02011 
02012 bool cGlobal::Active(bool log)
02013 {
02014   cMutexLock lock(&cams.listMutex);
02015   for(cCam *c=cams.First(); c; c=cams.Next(c)) if(c->Active(log)) return true;
02016   return false;
02017 }
02018 
02019 void cGlobal::CaidsChanged(void)
02020 {
02021   cMutexLock lock(&cams.listMutex);
02022   PRINTF(L_CORE_CAIDS,"caid list rebuild triggered");
02023   for(cCam *c=cams.First(); c; c=cams.Next(c)) c->CaidsChanged();
02024 }
02025 
02026 // -- cCam ---------------------------------------------------------------
02027 
02028 #ifndef SASC
02029 struct TPDU {
02030   unsigned char slot;
02031   unsigned char tcid;
02032   unsigned char tag;
02033   unsigned char len;
02034   unsigned char data[1];
02035   };
02036 #endif
02037 
02038 cCam::cCam(cDevice *Device, int Adapter, int Frontend, const char *DevId, cScDevicePlugin *DevPlugin, bool SoftCSA, bool FullTS)
02039 {
02040   device=Device; devplugin=DevPlugin; adapter=Adapter; frontend=Frontend; devId=DevId;
02041   softcsa=SoftCSA; fullts=FullTS;
02042   tcid=0; rebuildcaids=false;
02043   memset(version,0,sizeof(version));
02044 #ifndef SASC
02045   memset(slots,0,sizeof(slots));
02046   SetDescription("SC-CI adapter on device %s",devId);
02047   rb=new cRingBufferLinear(KILOBYTE(8),6+LEN_OFF,false,"SC-CI adapter read");
02048   if(rb) {
02049     rb->SetTimeouts(0,CAM_READ_TIMEOUT);
02050     frame.SetRb(rb);
02051     BuildCaids(true);
02052     slots[0]=new cScCamSlot(this,devId,0);
02053     Start();
02054     }
02055   else PRINTF(L_GEN_ERROR,"failed to create ringbuffer for SC-CI adapter %s.",devId);
02056 
02057   decsa=softcsa ? new cDeCSA(devId) : 0;
02058 #endif //!SASC
02059 
02060   source=transponder=-1; liveVpid=liveApid=0; logger=0; hookman=0;
02061   memset(lastCW,0,sizeof(lastCW));
02062   memset(indexMap,0,sizeof(indexMap));
02063   memset(splitSid,0,sizeof(splitSid));
02064   cams.Register(this);
02065 }
02066 
02067 cCam::~cCam()
02068 {
02069   cams.Unregister(this);
02070   handlerList.Clear();
02071   delete hookman;
02072   delete logger;
02073 #ifndef SASC
02074   Cancel(3);
02075   ciMutex.Lock();
02076   delete rb; rb=0;
02077   ciMutex.Unlock();
02078   delete decsa; decsa=0;
02079 #endif //!SASC
02080 }
02081 
02082 #ifndef SASC
02083 bool cCam::OwnSlot(const cCamSlot *slot) const
02084 {
02085   if(slots[0]==slot) return true;
02086   //for(int i=0; i<MAX_CI_SLOTS; i++) if(slots[i]==slot) return true;
02087   return false;
02088 }
02089 
02090 int cCam::GetCaids(int slot, unsigned short *Caids, int max)
02091 {
02092   BuildCaids(false);
02093   cMutexLock lock(&ciMutex);
02094   if(Caids) {
02095     int i;
02096     for(i=0; i<MAX_CI_SLOT_CAIDS && i<max && caids[i]; i++) Caids[i]=caids[slot][i];
02097     Caids[i]=0;
02098     }
02099   return version[slot];
02100 }
02101 #endif //!SASC
02102 
02103 void cCam::CaidsChanged(void)
02104 {
02105   rebuildcaids=true;
02106 }
02107 
02108 #ifndef SASC
02109 void cCam::BuildCaids(bool force)
02110 {
02111   if(caidTimer.TimedOut() || force || (rebuildcaids && triggerTimer.TimedOut())) {
02112     PRINTF(L_CORE_CAIDS,"%s: building caid lists",devId);
02113     cChannelList list(devId);
02114     Channels.Lock(false);
02115     for(cChannel *channel=Channels.First(); channel; channel=Channels.Next(channel)) {
02116       if(!channel->GroupSep() && channel->Ca()>=CA_ENCRYPTED_MIN && device->ProvidesTransponder(channel)) {
02117         cChannelCaids *ch=new cChannelCaids(channel);
02118         if(ch) list.Add(ch);
02119         }
02120       }
02121     Channels.Unlock();
02122     list.Unique(true);
02123     list.CheckIgnore();
02124     list.Unique(false);
02125 
02126     int n=0, h;
02127     caid_t c[MAX_CI_SLOT_CAIDS+1];
02128     memset(c,0,sizeof(c));
02129     do {
02130       if((h=list.Histo())<0) break;
02131       c[n++]=h;
02132       LBSTART(L_CORE_CAIDS);
02133       LBPUT("%s: added %04x caids now",devId,h); for(int i=0; i<n; i++) LBPUT(" %04x",c[i]);
02134       LBEND();
02135       list.Purge(h,false);
02136       } while(n<MAX_CI_SLOT_CAIDS && list.Count()>0);
02137     c[n]=0;
02138     if(n==0) PRINTF(L_CORE_CI,"%s: no active CAIDs",devId);
02139     else if(list.Count()>0) PRINTF(L_GEN_ERROR,"too many CAIDs. You should ignore some CAIDs.");
02140 
02141     ciMutex.Lock();
02142     if((version[0]==0 && c[0]!=0) || memcmp(caids[0],c,sizeof(caids[0]))) {
02143       memcpy(caids[0],c,sizeof(caids[0]));
02144       version[0]++;
02145       if(version[0]>0) {
02146         LBSTART(L_CORE_CI);
02147         LBPUT("card %s, slot %d (v=%2d) caids:",devId,0,version[0]);
02148         for(int i=0; caids[0][i]; i++) LBPUT(" %04x",caids[0][i]);
02149         LBEND();
02150         }
02151       }
02152     ciMutex.Unlock();
02153 
02154     caidTimer.Set(CAID_TIME);
02155     triggerTimer.Set(TRIGGER_TIME);
02156     rebuildcaids=false;
02157     }
02158 }
02159 
02160 int cCam::Read(unsigned char *Buffer, int MaxLength)
02161 {
02162   cMutexLock lock(&ciMutex);
02163   if(rb && Buffer && MaxLength>0) {
02164     int s;
02165     unsigned char *data=frame.Get(s);
02166     if(data) {
02167       if(s<=MaxLength) memcpy(Buffer,data,s);
02168       else PRINTF(L_GEN_DEBUG,"internal: sc-ci %s rb frame size exceeded %d",devId,s);
02169       frame.Del();
02170       if(Buffer[2]!=0x80 || LOG(L_CORE_CIFULL)) {
02171         LDUMP(L_CORE_CI,Buffer,s,"%s.%d <-",devId,Buffer[0]);
02172         readTimer.Set();
02173         }
02174       return s;
02175       }
02176     }
02177   else cCondWait::SleepMs(CAM_READ_TIMEOUT);
02178   if(LOG(L_CORE_CIFULL) && readTimer.Elapsed()>2000) {
02179     PRINTF(L_CORE_CIFULL,"%s: read heartbeat",devId);
02180     readTimer.Set();
02181     }
02182   return 0;
02183 }
02184 
02185 #define TPDU(data,slot)   do { unsigned char *_d=(data); _d[0]=(slot); _d[1]=tcid; } while(0)
02186 #define TAG(data,tag,len) do { unsigned char *_d=(data); _d[0]=(tag); _d[1]=(len); } while(0)
02187 #define SB_TAG(data,sb)   do { unsigned char *_d=(data); _d[0]=0x80; _d[1]=0x02; _d[2]=tcid; _d[3]=(sb); } while(0)
02188 
02189 void cCam::Write(const unsigned char *buff, int len)
02190 {
02191   cMutexLock lock(&ciMutex);
02192   if(buff && len>=5) {
02193     struct TPDU *tpdu=(struct TPDU *)buff;
02194     int slot=tpdu->slot;
02195     cCiFrame *slotframe=slots[slot]->Frame();
02196     if(buff[2]!=0xA0 || buff[3]>0x01 || LOG(L_CORE_CIFULL))
02197       LDUMP(L_CORE_CI,buff,len,"%s.%d ->",devId,slot);
02198     if(slots[slot]) {
02199       switch(tpdu->tag) {
02200         case 0x81: // T_RCV
02201           {
02202           int s;
02203           unsigned char *d=slotframe->Get(s);
02204           if(d) {
02205             unsigned char *b;
02206             if((b=frame.GetBuff(s+6))) {
02207               TPDU(b,slot);
02208               memcpy(b+2,d,s);
02209               slotframe->Del(); // delete from rb before Avail()
02210               SB_TAG(b+2+s,slotframe->Avail()>0 ? 0x80:0x00);
02211               frame.Put();
02212               }
02213             else slotframe->Del();
02214             }
02215           break;
02216           }
02217         case 0x82: // T_CREATE_TC
02218           {
02219           tcid=tpdu->data[0];
02220           unsigned char *b;
02221           static const unsigned char reqCAS[] = { 0xA0,0x07,0x01,0x91,0x04,0x00,0x03,0x00,0x41 };
02222           if((b=slotframe->GetBuff(sizeof(reqCAS)))) {
02223             memcpy(b,reqCAS,sizeof(reqCAS));
02224             b[2]=tcid;
02225             slotframe->Put();
02226             }
02227           if((b=frame.GetBuff(9))) {
02228             TPDU(b,slot);
02229             TAG(&b[2],0x83,0x01); b[4]=tcid;
02230             SB_TAG(&b[5],slotframe->Avail()>0 ? 0x80:0x00);
02231             frame.Put();
02232             }
02233           break;
02234           }
02235         case 0xA0: // T_DATA_LAST
02236           {
02237           slots[slot]->Process(buff,len);
02238           unsigned char *b;
02239           if((b=frame.GetBuff(6))) {
02240             TPDU(b,slot);
02241             SB_TAG(&b[2],slotframe->Avail()>0 ? 0x80:0x00);
02242             frame.Put();
02243             }
02244           break;
02245           }
02246         }
02247       }
02248     }
02249   else PRINTF(L_CORE_CIFULL,"%s: short write (buff=%d len=%d)",devId,buff!=0,len);
02250 }
02251 
02252 bool cCam::Reset(int Slot)
02253 {
02254   cMutexLock lock(&ciMutex);
02255   PRINTF(L_CORE_CI,"%s: reset of slot %d requested",devId,Slot);
02256   return slots[Slot] ? slots[Slot]->Reset():false;
02257 }
02258 
02259 eModuleStatus cCam::ModuleStatus(int Slot)
02260 {
02261   cMutexLock lock(&ciMutex);
02262   bool enable=ScSetup.CapCheck(device->CardIndex());
02263   if(!enable) Stop();
02264   return (enable && slots[Slot]) ? slots[Slot]->Status():msNone;
02265 }
02266 
02267 bool cCam::Assign(cDevice *Device, bool Query)
02268 {
02269   return Device ? (Device==device) : true;
02270 }
02271 #endif //!SASC
02272 
02273 bool cCam::IsSoftCSA(bool live)
02274 {
02275   return softcsa && (!fullts || !live);
02276 }
02277 
02278 void cCam::Tune(const cChannel *channel)
02279 {
02280   cMutexLock lock(&camMutex);
02281   if(source!=channel->Source() || transponder!=channel->Transponder()) {
02282     source=channel->Source(); transponder=channel->Transponder();
02283     PRINTF(L_CORE_PIDS,"%s: now tuned to source %x(%s) transponder %x",devId,source,*cSource::ToString(source),transponder);
02284     Stop();
02285     }
02286   else PRINTF(L_CORE_PIDS,"%s: tune to same source/transponder",devId);
02287 }
02288 
02289 void cCam::PostTune(void)
02290 {
02291   if(ScSetup.PrestartAU) {
02292     LogStartup();
02293     if(logger) logger->PreScan(source,transponder);
02294     }
02295 }
02296 
02297 void cCam::SetPid(int type, int pid, bool on)
02298 {
02299   cMutexLock lock(&camMutex);
02300   int oldA=liveApid, oldV=liveVpid;
02301   if(type==1) liveVpid=on ? pid:0;
02302   else if(type==0) liveApid=on ? pid:0;
02303   else if(liveVpid==pid && on) liveVpid=0;
02304   else if(liveApid==pid && on) liveApid=0;
02305   if(oldA!=liveApid || oldV!=liveVpid)
02306     PRINTF(L_CORE_PIDS,"%s: livepids video=%04x audio=%04x",devId,liveVpid,liveApid);
02307 }
02308 
02309 void cCam::Stop(void)
02310 {
02311   cMutexLock lock(&camMutex);
02312   for(cEcmHandler *handler=handlerList.First(); handler; handler=handlerList.Next(handler))
02313     handler->Stop();
02314   if(logger) logger->Down();
02315   if(hookman) hookman->Down();
02316   memset(splitSid,0,sizeof(splitSid));
02317 }
02318 
02319 void cCam::AddPrg(cPrg *prg)
02320 {
02321   cMutexLock lock(&camMutex);
02322   bool islive=false;
02323   for(cPrgPid *pid=prg->pids.First(); pid; pid=prg->pids.Next(pid))
02324     if(pid->pid==liveVpid || pid->pid==liveApid) {
02325       islive=true;
02326       break;
02327       }
02328   bool needZero=!IsSoftCSA(islive) && (islive || !ScSetup.ConcurrentFF);
02329   bool noshift=IsSoftCSA(true) || (prg->IsUpdate() && prg->pids.Count()==0);
02330   PRINTF(L_CORE_PIDS,"%s: %s SID %d (zero=%d noshift=%d)",devId,prg->IsUpdate()?"update":"add",prg->sid,needZero,noshift);
02331   if(prg->pids.Count()>0) {
02332     LBSTART(L_CORE_PIDS);
02333     LBPUT("%s: pids",devId);
02334     for(cPrgPid *pid=prg->pids.First(); pid; pid=prg->pids.Next(pid))
02335       LBPUT(" %s=%04x",TYPENAME(pid->type),pid->pid);
02336     LBEND();
02337     }
02338   bool isSplit=false;
02339   if(prg->pids.Count()>0 && prg->SimplifyCaDescr()) isSplit=true;
02340   else {
02341     for(int i=0; splitSid[i]; i++)
02342       if(splitSid[i]==prg->sid) { isSplit=true; break; }
02343     }
02344   if(!isSplit) {
02345     cEcmHandler *handler=GetHandler(prg->sid,needZero,noshift);
02346     if(handler) {
02347       PRINTF(L_CORE_PIDS,"%s: found handler for SID %d (%s idle=%d idx=%d)",devId,prg->sid,handler->Id(),handler->IsIdle(),handler->CwIndex());
02348       prg->source=source;
02349       prg->transponder=transponder;
02350       handler->SetPrg(prg);
02351       }
02352     }
02353   else {
02354     PRINTF(L_CORE_PIDS,"%s: SID %d is handled as splitted",devId,prg->sid);
02355     // first update the splitSid list
02356     if(prg->pids.Count()==0) { // delete
02357       for(int i=0; splitSid[i]; i++)
02358         if(splitSid[i]==prg->sid) {
02359           memmove(&splitSid[i],&splitSid[i+1],sizeof(splitSid[0])*(MAX_SPLIT_SID-i));
02360           break;
02361           }
02362       PRINTF(L_CORE_PIDS,"%s: deleted from list",devId);
02363       }
02364     else { // add
02365       bool has=false;
02366       int i;
02367       for(i=0; splitSid[i]; i++) if(splitSid[i]==prg->sid) has=true;
02368       if(!has) {
02369         if(i<MAX_SPLIT_SID) {
02370           splitSid[i]=prg->sid;
02371           splitSid[i+1]=0;
02372           PRINTF(L_CORE_PIDS,"%s: added to list",devId);
02373           }
02374         else PRINTF(L_CORE_PIDS,"%s: split SID list overflow",devId);
02375         }
02376       }
02377     LBSTART(L_CORE_PIDS);
02378     LBPUT("%s: split SID list now:",devId);
02379     for(int i=0; i<=MAX_SPLIT_SID; i++) LBPUT(" %d",splitSid[i]);
02380     LBEND();
02381     // prepare an empty prg head
02382     cPrg work;
02383     work.source=source;
02384     work.transponder=transponder;
02385     // loop through pids
02386     int group=1;
02387     cPrgPid *first;
02388     while((first=prg->pids.First())) {
02389       LBSTARTF(L_CORE_PIDS);
02390       LBPUT("%s: group %d pids",devId,group);
02391       prg->pids.Del(first,false);
02392       work.caDescr.Set(&first->caDescr);
02393       first->caDescr.Clear();
02394       work.pids.Add(first);
02395       LBPUT(" %04x",first->pid);
02396       for(cPrgPid *pid=prg->pids.First(); pid;) {
02397         cPrgPid *next=prg->pids.Next(pid);
02398         if(work.caDescr==pid->caDescr) { // same group
02399           prg->pids.Del(pid,false);
02400           pid->caDescr.Clear();
02401           work.pids.Add(pid);
02402           LBPUT(" %04x",pid->pid);
02403           }
02404         pid=next;
02405         }
02406       LBEND();
02407       // get a handler for the group
02408       int grsid=group*SIDGRP_SHIFT+prg->sid;
02409       cEcmHandler *handler=0;
02410       if(group==1) {
02411         // in the first group check if we have a non-split handler
02412         // for the sid
02413         for(handler=handlerList.First(); handler; handler=handlerList.Next(handler))
02414           if(handler->Sid()==prg->sid) {
02415             // let GetHandler() take care of needZero/noshift stuff
02416             handler=GetHandler(prg->sid,needZero,noshift);
02417             break;
02418             }
02419         }
02420       // otherwise get the group-sid handler
02421       if(!handler) handler=GetHandler(grsid,needZero,noshift);
02422       if(handler) {
02423         PRINTF(L_CORE_PIDS,"%s: found handler for group-SID %d (%s idle=%d idx=%d)",devId,grsid,handler->Id(),handler->IsIdle(),handler->CwIndex());
02424         work.sid=grsid;
02425         handler->SetPrg(&work);
02426         }
02427       // prepare for next group
02428       work.pids.Clear();
02429       needZero=false; // only one group can have this
02430       group++;
02431       }
02432     // now we scan the handler list for leftover group handlers
02433     for(cEcmHandler *handler=handlerList.First(); handler; handler=handlerList.Next(handler)) {
02434       int sid=handler->Sid();
02435       if(!handler->IsIdle() && sid>SIDGRP_SHIFT) {
02436         int gr=sid/SIDGRP_SHIFT;
02437         sid%=SIDGRP_SHIFT;
02438         if(sid==prg->sid && gr>=group) {
02439           PRINTF(L_CORE_PIDS,"%s: idle group handler %s idx=%d",devId,handler->Id(),handler->CwIndex());
02440           work.sid=handler->Sid();
02441           handler->SetPrg(&work);
02442           }
02443         }
02444       }
02445     }
02446 }
02447 
02448 bool cCam::HasPrg(int prg)
02449 {
02450   cMutexLock lock(&camMutex);
02451   for(cEcmHandler *handler=handlerList.First(); handler; handler=handlerList.Next(handler))
02452     if(!handler->IsIdle() && handler->Sid()==prg)
02453       return true;
02454   return false;
02455 }
02456 
02457 char *cCam::CurrentKeyStr(int num, const char **id)
02458 {
02459   cMutexLock lock(&camMutex);
02460   if(id) *id=devId;
02461   cEcmHandler *handler;
02462   for(handler=handlerList.First(); handler; handler=handlerList.Next(handler))
02463     if(--num<0) return handler->CurrentKeyStr();
02464   return 0;
02465 }
02466 
02467 bool cCam::Active(bool log)
02468 {
02469   cMutexLock lock(&camMutex);
02470   for(cEcmHandler *handler=handlerList.First(); handler; handler=handlerList.Next(handler))
02471     if(!handler->IsIdle()) {
02472       if(log) PRINTF(L_GEN_INFO,"handler %s on card %s is not idle",handler->Id(),devId);
02473       return true;
02474       }
02475   return false;
02476 }
02477 
02478 void cCam::HouseKeeping(void)
02479 {
02480   cMutexLock lock(&camMutex);
02481   for(cEcmHandler *handler=handlerList.First(); handler;) {
02482     cEcmHandler *next=handlerList.Next(handler);
02483     if(handler->IsRemoveable()) RemHandler(handler);
02484     handler=next;
02485     }
02486   if(handlerList.Count()<1 && !ScSetup.PrestartAU) {
02487     delete hookman; hookman=0;
02488     delete logger; logger=0;
02489     }
02490 }
02491 
02492 void cCam::LogStartup(void)
02493 {
02494   cMutexLock lock(&camMutex);
02495   if(!logger && ScSetup.AutoUpdate) {
02496     logger=new cLogger(this,device,devId,IsSoftCSA(false));
02497     LogStatsUp();
02498     }
02499 }
02500 
02501 void cCam::LogEcmStatus(const cEcmInfo *ecm, bool on)
02502 {
02503   cMutexLock lock(&camMutex);
02504   if(on) LogStartup();
02505   if(logger) logger->EcmStatus(ecm,on);
02506 }
02507 
02508 void cCam::AddHook(cLogHook *hook)
02509 {
02510   cMutexLock lock(&camMutex);
02511   if(!hookman) hookman=new cHookManager(device,devId);
02512   if(hookman) hookman->AddHook(hook);
02513 }
02514 
02515 bool cCam::TriggerHook(int id)
02516 {
02517   return hookman && hookman->TriggerHook(id);
02518 }
02519 
02520 void cCam::SetCWIndex(int pid, int index)
02521 {
02522   if(index<MAX_CW_IDX) {
02523     ca_pid_t ca_pid;
02524     ca_pid.pid=pid;
02525     ca_pid.index=index;
02526     PRINTF(L_CORE_PIDS,"%s: descrambling pid %04x on index %x",devId,pid,index);
02527     if(!SetCaPid(&ca_pid))
02528       if(index>0) {
02529         PRINTF(L_GEN_ERROR,"CA_SET_PID failed (%s). Expect a black screen/bad recording. Do you use the patched DVB driver?",strerror(errno));
02530         PRINTF(L_GEN_WARN,"Adjusting 'Concurrent FF streams' to NO");
02531         ScSetup.ConcurrentFF=0;
02532         ScSetup.Store(true);
02533         }
02534     }
02535 }
02536 
02537 void cCam::WriteCW(int index, unsigned char *cw, bool force)
02538 {
02539   if(index<MAX_CW_IDX) {
02540     for(int i=0; i<16; i+=4) cw[i+3]=cw[i]+cw[i+1]+cw[i+2];
02541     ca_descr_t ca_descr;
02542     ca_descr.index=index;
02543     unsigned char *last=lastCW[index];
02544     if(force || memcmp(&cw[0],&last[0],8)) {
02545       memcpy(&last[0],&cw[0],8);
02546       ca_descr.parity=0;
02547       memcpy(ca_descr.cw,&cw[0],8);
02548       if(!SetCaDescr(&ca_descr,force))
02549         PRINTF(L_GEN_ERROR,"CA_SET_DESCR failed (%s). Expect a black screen.",strerror(errno));
02550       }
02551 
02552     if(force || memcmp(&cw[8],&last[8],8)) {
02553       memcpy(&last[8],&cw[8],8);
02554       ca_descr.parity=1;
02555       memcpy(ca_descr.cw,&cw[8],8);
02556       if(!SetCaDescr(&ca_descr,force))
02557         PRINTF(L_GEN_ERROR,"CA_SET_DESCR failed (%s). Expect a black screen.",strerror(errno));
02558       }
02559     }
02560 }
02561 
02562 bool cCam::SetCaDescr(ca_descr_t *ca_descr, bool initial)
02563 {
02564   if(!softcsa || (fullts && ca_descr->index==0))
02565     return devplugin->SetCaDescr(device,ca_descr,initial);
02566 #ifndef SASC
02567   else if(decsa)
02568     return decsa->SetDescr(ca_descr,initial);
02569 #endif //!SASC
02570   return false;
02571 }
02572 
02573 bool cCam::SetCaPid(ca_pid_t *ca_pid)
02574 {
02575   if(!softcsa || (fullts && ca_pid->index==0))
02576     return devplugin->SetCaPid(device,ca_pid);
02577 #ifndef SASC
02578   else if(decsa)
02579     return decsa->SetCaPid(ca_pid);
02580 #endif //!SASC
02581   return false;
02582 }
02583 
02584 void cCam::DumpAV7110(void)
02585 {
02586   devplugin->DumpAV(device);
02587 }
02588 
02589 int cCam::GetFreeIndex(void)
02590 {
02591   for(int idx=0; idx<MAX_CW_IDX; idx++)
02592     if(!indexMap[idx]) return idx;
02593   return -1;
02594 }
02595 
02596 cEcmHandler *cCam::GetHandler(int sid, bool needZero, bool noshift)
02597 {
02598   cEcmHandler *zerohandler=0, *sidhandler=0, *idlehandler=0;
02599   for(cEcmHandler *handler=handlerList.First(); handler; handler=handlerList.Next(handler)) {
02600     if(handler->Sid()==sid)
02601       sidhandler=handler;
02602     if(handler->CwIndex()==0)
02603       zerohandler=handler;
02604     if(handler->IsIdle() && (!idlehandler || (!(needZero ^ (idlehandler->CwIndex()!=0)) && (needZero ^ (handler->CwIndex()!=0))) ))
02605       idlehandler=handler;
02606     }
02607   LBSTART(L_CORE_PIDS);
02608   LBPUT("%s: SID=%d zero=%d |",devId,sid,needZero);
02609   if(sidhandler) LBPUT(" sid=%d/%d/%d",sidhandler->CwIndex(),sidhandler->Sid(),sidhandler->IsIdle());
02610   else LBPUT(" sid=-/-/-");
02611   if(zerohandler) LBPUT(" zero=%d/%d/%d",zerohandler->CwIndex(),zerohandler->Sid(),zerohandler->IsIdle());
02612   else LBPUT(" zero=-/-/-");
02613   if(idlehandler) LBPUT(" idle=%d/%d/%d",idlehandler->CwIndex(),idlehandler->Sid(),idlehandler->IsIdle());
02614   else LBPUT(" idle=-/-/-");
02615   LBEND();
02616 
02617   if(sidhandler) {
02618     if(needZero && sidhandler->CwIndex()!=0 && !noshift) {
02619       if(!sidhandler->IsIdle())
02620         PRINTF(L_CORE_ECM,"%s: shifting cwindex on non-idle handler.",devId);
02621       if(zerohandler) {
02622         if(!zerohandler->IsIdle())
02623           PRINTF(L_CORE_ECM,"%s: shifting non-idle zero handler. This shouldn't happen!",devId);
02624         zerohandler->ShiftCwIndex(sidhandler->CwIndex());
02625         sidhandler->ShiftCwIndex(0);
02626         }
02627       else if(indexMap[0]==0) {
02628         indexMap[0]=1;
02629         indexMap[sidhandler->CwIndex()]=0;
02630         sidhandler->ShiftCwIndex(0);
02631         }
02632       else PRINTF(L_CORE_ECM,"%s: zero index not free.",devId);
02633       }
02634 
02635     if(!needZero && sidhandler->CwIndex()==0 && !noshift) {
02636       if(!sidhandler->IsIdle())
02637         PRINTF(L_CORE_ECM,"%s: shifting cwindex on non-idle handler.",devId);
02638       int idx=GetFreeIndex();
02639       if(idx>=0) {
02640         indexMap[idx]=1;
02641         sidhandler->ShiftCwIndex(idx);
02642         indexMap[0]=0;
02643         }
02644       else PRINTF(L_CORE_ECM,"%s: no free cwindex. Can't free zero index.",devId);
02645       }
02646 
02647     return sidhandler;
02648     }
02649 
02650   if(needZero && zerohandler) {
02651     if(!zerohandler->IsIdle())
02652       PRINTF(L_CORE_ECM,"%s: changing SID on non-idle zero handler. This shouldn't happen!",devId);
02653     return zerohandler;
02654     }
02655   
02656   if(idlehandler) {
02657     if(needZero && idlehandler->CwIndex()!=0 && !noshift) {
02658       if(indexMap[0]==0) {
02659         indexMap[0]=1;
02660         indexMap[idlehandler->CwIndex()]=0;
02661         idlehandler->ShiftCwIndex(0);
02662         }
02663       else PRINTF(L_CORE_ECM,"%s: zero index not free. (2)",devId);
02664       }
02665     if(!needZero && idlehandler->CwIndex()==0 && !noshift) {
02666       int idx=GetFreeIndex();
02667       if(idx>=0) {
02668         indexMap[idx]=1;
02669         idlehandler->ShiftCwIndex(idx);
02670         indexMap[0]=0;
02671         }
02672       else PRINTF(L_CORE_ECM,"%s: no free cwindex. Can't free zero index. (2)",devId);
02673       }
02674     if((needZero ^ (idlehandler->CwIndex()==0)))
02675       PRINTF(L_CORE_ECM,"%s: idlehandler index doesn't match needZero",devId);
02676     return idlehandler;
02677     }
02678 
02679   int idx=GetFreeIndex();
02680   if(!needZero && idx==0) {
02681     indexMap[0]=1;
02682     idx=GetFreeIndex();
02683     indexMap[0]=0;
02684     if(idx<0) {
02685       idx=0;
02686       PRINTF(L_CORE_ECM,"%s: can't respect !needZero for new handler",devId);
02687       }
02688     }
02689   if(idx<0) {
02690     PRINTF(L_CORE_ECM,"%s: no free cwindex",devId);
02691     return 0;
02692     }
02693   indexMap[idx]=1;
02694   idlehandler=new cEcmHandler(this,device,devId,idx);
02695   handlerList.Add(idlehandler);
02696   return idlehandler;
02697 }
02698 
02699 void cCam::RemHandler(cEcmHandler *handler)
02700 {
02701   int idx=handler->CwIndex();
02702   PRINTF(L_CORE_PIDS,"%s: removing %s on cw index %d",devId,handler->Id(),idx);
02703   handlerList.Del(handler);
02704   indexMap[idx]=0;
02705 }
02706 
02707 // -- cDeCSA -------------------------------------------------------------------
02708 
02709 #ifndef SASC
02710 
02711 #define MAX_STALL_MS 70
02712 
02713 #define MAX_REL_WAIT 100 // time to wait if key in used on set
02714 #define MAX_KEY_WAIT 500 // time to wait if key not ready on change
02715 
02716 #define FL_EVEN_GOOD 1
02717 #define FL_ODD_GOOD  2
02718 #define FL_ACTIVITY  4
02719 
02720 
02721 cDeCSA::cDeCSA(const char *DevId)
02722 :stall(MAX_STALL_MS)
02723 {
02724   devId=DevId;
02725   cs=get_suggested_cluster_size();
02726   PRINTF(L_CORE_CSA,"%s: clustersize=%d rangesize=%d",devId,cs,cs*2+5);
02727   range=MALLOC(unsigned char *,(cs*2+5));
02728   memset(keys,0,sizeof(keys));
02729   memset(pidmap,0,sizeof(pidmap));
02730   ResetState();
02731 }
02732 
02733 cDeCSA::~cDeCSA()
02734 {
02735   for(int i=0; i<MAX_CSA_IDX; i++)
02736     if(keys[i]) free_key_struct(keys[i]);
02737   free(range);
02738 }
02739 
02740 void cDeCSA::ResetState(void)
02741 {
02742   PRINTF(L_CORE_CSA,"%s: reset state",devId);
02743   memset(even_odd,0,sizeof(even_odd));
02744   memset(flags,0,sizeof(flags));
02745   lastData=0;
02746 }
02747 
02748 void cDeCSA::SetActive(bool on)
02749 {
02750   if(!on && active) ResetState();
02751   active=on;
02752   PRINTF(L_CORE_CSA,"%s: set active %s",devId,active?"on":"off");
02753 }
02754 
02755 bool cDeCSA::GetKeyStruct(int idx)
02756 {
02757   if(!keys[idx]) keys[idx]=get_key_struct();
02758   return keys[idx]!=0;
02759 }
02760 
02761 bool cDeCSA::SetDescr(ca_descr_t *ca_descr, bool initial)
02762 {
02763   cMutexLock lock(&mutex);
02764   int idx=ca_descr->index;
02765   if(idx<MAX_CSA_IDX && GetKeyStruct(idx)) {
02766     if(!initial && active && ca_descr->parity==(even_odd[idx]&0x40)>>6) {
02767       if(flags[idx] & (ca_descr->parity?FL_ODD_GOOD:FL_EVEN_GOOD)) {
02768         PRINTF(L_CORE_CSA,"%s.%d: %s key in use (%d ms)",devId,idx,ca_descr->parity?"odd":"even",MAX_REL_WAIT);
02769         if(wait.TimedWait(mutex,MAX_REL_WAIT)) PRINTF(L_CORE_CSA,"%s.%d: successfully waited for release",devId,idx);
02770         else PRINTF(L_CORE_CSA,"%s.%d: timed out. setting anyways",devId,idx);
02771         }
02772       else PRINTF(L_CORE_CSA,"%s.%d: late key set...",devId,idx);
02773       }
02774     LDUMP(L_CORE_CSA,ca_descr->cw,8,"%s.%d: %4s key set",devId,idx,ca_descr->parity?"odd":"even");
02775     if(ca_descr->parity==0) {
02776       set_even_control_word(keys[idx],ca_descr->cw);
02777       if(!CheckNull(ca_descr->cw,8)) flags[idx]|=FL_EVEN_GOOD|FL_ACTIVITY;
02778       else PRINTF(L_CORE_CSA,"%s.%d: zero even CW",devId,idx);
02779       wait.Broadcast();
02780       }
02781     else {
02782       set_odd_control_word(keys[idx],ca_descr->cw);
02783       if(!CheckNull(ca_descr->cw,8)) flags[idx]|=FL_ODD_GOOD|FL_ACTIVITY;
02784       else PRINTF(L_CORE_CSA,"%s.%d: zero odd CW",devId,idx);
02785       wait.Broadcast();
02786       }
02787     }
02788   return true;
02789 }
02790 
02791 bool cDeCSA::SetCaPid(ca_pid_t *ca_pid)
02792 {
02793   cMutexLock lock(&mutex);
02794   if(ca_pid->index<MAX_CSA_IDX && ca_pid->pid<MAX_CSA_PIDS) {
02795     pidmap[ca_pid->pid]=ca_pid->index;
02796     PRINTF(L_CORE_CSA,"%s.%d: set pid %04x",devId,ca_pid->index,ca_pid->pid);
02797     }
02798   return true;
02799 }
02800 
02801 bool cDeCSA::Decrypt(unsigned char *data, int len, bool force)
02802 {
02803   cMutexLock lock(&mutex);
02804   int r=-2, ccs=0, currIdx=-1;
02805   bool newRange=true;
02806   range[0]=0;
02807   len-=(TS_SIZE-1);
02808   int l;
02809   for(l=0; l<len; l+=TS_SIZE) {
02810     if(data[l]!=TS_SYNC_BYTE) {       // let higher level cope with that
02811       PRINTF(L_CORE_CSA,"%s: garbage in TS buffer",devId);
02812       if(ccs) force=true;             // prevent buffer stall
02813       break;
02814       }
02815     unsigned int ev_od=data[l+3]&0xC0;
02816     if(ev_od==0x80 || ev_od==0xC0) { // encrypted
02817       int idx=pidmap[((data[l+1]<<8)+data[l+2])&(MAX_CSA_PIDS-1)];
02818       if(currIdx<0 || idx==currIdx) { // same or no index
02819         currIdx=idx;
02820         if(ccs==0 && ev_od!=even_odd[idx]) {
02821           even_odd[idx]=ev_od;
02822           wait.Broadcast();
02823           PRINTF(L_CORE_CSA,"%s.%d: change to %s key",devId,idx,(ev_od&0x40)?"odd":"even");
02824           bool doWait=false;
02825           if(ev_od&0x40) {
02826             flags[idx]&=~FL_EVEN_GOOD;
02827             if(!(flags[idx]&FL_ODD_GOOD)) doWait=true;
02828             }
02829           else {
02830             flags[idx]&=~FL_ODD_GOOD;
02831             if(!(flags[idx]&FL_EVEN_GOOD)) doWait=true;
02832             }
02833           if(doWait) {
02834             PRINTF(L_CORE_CSA,"%s.%d: %s key not ready (%d ms)",devId,idx,(ev_od&0x40)?"odd":"even",MAX_KEY_WAIT);
02835             if(flags[idx]&FL_ACTIVITY) {
02836               flags[idx]&=~FL_ACTIVITY;
02837               if(wait.TimedWait(mutex,MAX_KEY_WAIT)) PRINTF(L_CORE_CSA,"%s.%d: successfully waited for key",devId,idx);
02838               else PRINTF(L_CORE_CSA,"%s.%d: timed out. proceeding anyways",devId,idx);
02839               }
02840             else PRINTF(L_CORE_CSA,"%s.%d: not active. wait skipped",devId,idx);
02841             }
02842           }
02843         if(newRange) {
02844           r+=2; newRange=false;
02845           range[r]=&data[l];
02846           range[r+2]=0;
02847           }
02848         range[r+1]=&data[l+TS_SIZE];
02849         if(++ccs>=cs) break;
02850         }
02851       else newRange=true;             // other index, create hole
02852       }
02853     else {                            // unencrypted
02854       // nothing, we don't create holes for unencrypted packets
02855       }
02856     }
02857   int scanTS=l/TS_SIZE;
02858   int stallP=ccs*100/scanTS;
02859 
02860   LBSTART(L_CORE_CSAVERB);
02861   LBPUT("%s: %s-%d-%d : %d-%d-%d stall=%d :: ",
02862         devId,data==lastData?"SAME":"MOVE",(len+(TS_SIZE-1))/TS_SIZE,force,
02863         currIdx,ccs,scanTS,stallP);
02864   for(int l=0; l<len; l+=TS_SIZE) {
02865     if(data[l]!=TS_SYNC_BYTE) break;
02866     unsigned int ev_od=data[l+3]&0xC0;
02867     if(ev_od&0x80) {
02868       int pid=(data[l+1]<<8)+data[l+2];
02869       int idx=pidmap[pid&(MAX_CSA_PIDS-1)];
02870       LBPUT("%s/%x/%d ",(ev_od&0x40)?"o":"e",pid,idx);
02871       }
02872     else {
02873       LBPUT("*/%x ",(data[l+1]<<8)+data[l+2]);
02874       }
02875     }
02876   LBEND();
02877 
02878   if(r>=0 && ccs<cs && !force) {
02879     if(lastData==data && stall.TimedOut()) {
02880       PRINTF(L_CORE_CSAVERB,"%s: stall timeout -> forced",devId);
02881       force=true;
02882       }
02883     else if(stallP<=10 && scanTS>=cs) {
02884       PRINTF(L_CORE_CSAVERB,"%s: flow factor stall -> forced",devId);
02885       force=true;
02886       }
02887     }
02888   lastData=data;
02889 
02890   if(r>=0) {                          // we have some range
02891     if(ccs>=cs || force) {
02892       if(GetKeyStruct(currIdx)) {
02893         int n=decrypt_packets(keys[currIdx],range);
02894         PRINTF(L_CORE_CSAVERB,"%s.%d: decrypting ccs=%3d cs=%3d %s -> %3d decrypted",devId,currIdx,ccs,cs,ccs>=cs?"OK ":"INC",n);
02895         if(n>0) {
02896           stall.Set(MAX_STALL_MS);
02897           return true;
02898           }
02899         }
02900       }
02901     else PRINTF(L_CORE_CSAVERB,"%s.%d: incomplete ccs=%3d cs=%3d",devId,currIdx,ccs,cs);
02902     }
02903   return false;
02904 }
02905 
02906 #endif //!SASC
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines