SASC
DVBSoftCAM

cccam.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 <stdio.h>
00021 #include <stdlib.h>
00022 #include <errno.h>
00023 #include <unistd.h>
00024 #include <sys/socket.h>
00025 #include <sys/un.h>
00026 
00027 #include <vdr/pat.h>
00028 
00029 #include "cc.h"
00030 
00031 #define LIST_ONLY 0x03   /* CA application should clear the list when an 'ONLY' CAPMT object is received, and start working with the object */
00032 
00033 // -- cCCcamCard ---------------------------------------------------------------
00034 
00035 class cCCcamCard : public cMutex {
00036 private:
00037   int ccam_fd;
00038   int cardnum, pid, pmtlen;
00039   bool newcw;
00040   cTimeMs timecw;
00041   unsigned char *capmt, cw[16];
00042   const char *path;
00043   cCondVar cwwait;
00044 public:
00045   cCCcamCard(void);
00046   ~cCCcamCard();
00047   void Setup(int num, const char *Path);
00048   bool Connect(void);
00049   void Disconnect(void);
00050   bool Connected(void) { return ccam_fd>=0; }
00051   void WriteCaPmt(void);
00052   void NewCaPmt(int p, const unsigned char *pmt, int len);
00053   int Pid(void) { return pid; }
00054   bool GetCw(unsigned char *Cw, int timeout);
00055   void NewCw(const unsigned char *Cw);
00056   };
00057 
00058 cCCcamCard::cCCcamCard(void)
00059 {
00060   cardnum=-1; path=0;
00061   ccam_fd=-1; capmt=0; newcw=false; pid=-1;
00062 }
00063 
00064 cCCcamCard::~cCCcamCard()
00065 {
00066   Disconnect();
00067   free(capmt);
00068 }
00069 
00070 void cCCcamCard::Setup(int num, const char *Path)
00071 {
00072   cardnum=num; path=Path;
00073 }
00074 
00075 void cCCcamCard::Disconnect(void)
00076 {
00077   cMutexLock lock(this);
00078   close(ccam_fd);
00079   ccam_fd=-1; newcw=false;
00080 }
00081 
00082 bool cCCcamCard::Connect(void)
00083 {
00084   cMutexLock lock(this);
00085   Disconnect();
00086   ccam_fd=socket(AF_LOCAL,SOCK_STREAM,0);
00087   if(!ccam_fd) {
00088     PRINTF(L_CC_CCCAM,"%d: socket failed: %s",cardnum,strerror(errno));
00089     return false;
00090     }
00091   sockaddr_un serv_addr_un;
00092   memset(&serv_addr_un,0,sizeof(serv_addr_un));
00093   serv_addr_un.sun_family=AF_LOCAL;
00094   snprintf(serv_addr_un.sun_path,sizeof(serv_addr_un.sun_path),path,cardnum);
00095   PRINTF(L_CC_CCCAM,"%d: using camd socket '%s'",cardnum,serv_addr_un.sun_path);
00096   if(connect(ccam_fd,(const sockaddr*)&serv_addr_un,sizeof(serv_addr_un))!=0) {
00097     PRINTF(L_CC_CCCAM,"%d: connect failed: %s",cardnum,strerror(errno));
00098     Disconnect();
00099     return false;
00100     }
00101   PRINTF(L_CC_CCCAM,"%d: opened camd socket",cardnum);
00102   return true;
00103 }
00104 
00105 void cCCcamCard::WriteCaPmt(void)
00106 {
00107   cMutexLock lock(this);
00108   if(capmt) {
00109     for(int retry=2; retry>0; retry--) {
00110       if(!Connected() && !Connect()) break;
00111       int r=write(ccam_fd,capmt,pmtlen);
00112       if(r==pmtlen) {
00113         newcw=false;
00114         break;
00115         }
00116       PRINTF(L_CC_CCCAM,"%d: write failed: %s",cardnum,strerror(errno));
00117       Disconnect();
00118       }
00119     }
00120 }
00121 
00122 void cCCcamCard::NewCaPmt(int p, const unsigned char *pmt, int len)
00123 {
00124   cMutexLock lock(this);
00125   free(capmt); pid=0; newcw=false;
00126   capmt=MALLOC(unsigned char,len);
00127   if(capmt) {
00128     memcpy(capmt,pmt,len);
00129     pmtlen=len;
00130     capmt[6]=LIST_ONLY;
00131     pid=p;
00132     WriteCaPmt();
00133     }
00134 }
00135 
00136 void cCCcamCard::NewCw(const unsigned char *Cw)
00137 {
00138   cMutexLock lock(this);
00139   if(memcmp(cw,Cw,sizeof(cw))) {
00140     memcpy(cw,Cw,sizeof(cw));
00141     newcw=true;
00142     timecw.Set();
00143     cwwait.Broadcast();
00144     }
00145 }
00146 
00147 bool cCCcamCard::GetCw(unsigned char *Cw, int timeout)
00148 {
00149   cMutexLock lock(this);
00150   if(newcw && timecw.Elapsed()>3000)
00151     newcw=false; // too old
00152   if(!newcw)
00153     cwwait.TimedWait(*this,timeout);
00154   if(newcw) {
00155     memcpy(Cw,cw,sizeof(cw));
00156     newcw=false;
00157     return true;
00158     }
00159   return false;
00160 }
00161 
00162 // -- cCardClientCCcam ---------------------------------------------------------
00163 
00164 class cCardClientCCcam : public cCardClient, private cThread {
00165 private:
00166   cCCcamCard card[4];
00167   int pmtversion;
00168   int failedcw;
00169   char *socketpath;
00170 protected:
00171   virtual void Action(void);
00172 public:
00173   cCardClientCCcam(const char *Name);
00174   ~cCardClientCCcam();
00175   virtual bool Init(const char *CfgDir);
00176   virtual bool Login(void);
00177   virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *Cw);
00178   };
00179 
00180 static cCardClientLinkReg<cCardClientCCcam> __ncd("cccam");
00181 
00182 cCardClientCCcam::cCardClientCCcam(const char *Name)
00183 :cCardClient(Name)
00184 ,cThread("CCcam listener")
00185 {
00186   pmtversion=0; socketpath=0;
00187   so.SetRWTimeout(2*1000);
00188   so.SetUDP(true);
00189 }
00190 
00191 cCardClientCCcam::~cCardClientCCcam()
00192 {
00193   Cancel(3);
00194   free(socketpath);
00195 }
00196 
00197 bool cCardClientCCcam::Init(const char *config)
00198 {
00199   cMutexLock lock(this);
00200   int num=0;
00201   char path[256];
00202   Logout();
00203   if(!ParseStdConfig(config,&num)
00204      || sscanf(&config[num],":%255[^:]",path)!=1) return false;
00205   PRINTF(L_CC_CORE,"%s: socket=%s",name,path);
00206   socketpath=strdup(path);
00207   for(int i=0; i<4; i++) card[i].Setup(i,socketpath);
00208   return true;
00209 }
00210 
00211 bool cCardClientCCcam::Login(void)
00212 {
00213   Logout();
00214   if(!so.Bind("127.0.0.1",port)) return false;
00215   so.SetQuietLog(true);
00216   PRINTF(L_CC_CCCAM,"Bound to port %d, starting UDP listener",port);
00217   Start();
00218   return true;
00219 }
00220 
00221 bool cCardClientCCcam::ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
00222 {
00223   cMutexLock lock(this);
00224   if((!so.Connected() && !Login()) || !CanHandle(ecm->caId)) return false;
00225 
00226   if(ecm->dvbAdapter<0 || ecm->dvbFrontend!=0) {
00227     PRINTF(L_CC_CCCAM,"cannot handle DVB adapter %d, frontend %d",ecm->dvbAdapter,ecm->dvbFrontend);
00228     return false;
00229     }
00230   int cardnum=ecm->dvbAdapter;
00231 
00232   cCCcamCard *c=&card[cardnum];
00233   int timeout=3000;
00234   if(ecm->ecm_pid!=c->Pid() || !c->Connected()) { // channel change
00235     int n;
00236     const unsigned char *descr=ecm->GetCaDescr(&n);
00237     if(!descr) {
00238       PRINTF(L_CC_CCCAM,"%d: no CA descriptor for caid %04x sid %d prov %04x",cardnum,ecm->caId,ecm->prgId,ecm->provId);
00239       return false;
00240       }
00241 
00242     static const unsigned char pmt[] = {
00243       0x9f,0x80,0x32,0x82,0xFF,0xFF,
00244       0x01,
00245 #define PRG_POS 7
00246       0xFF,0xFF,                                          // prg-nr
00247 #define VERS_POS 9
00248       0xFF,                                               // version
00249 #define LEN_POS 10
00250       0xFF,0xFF,                                          // prg-info-len
00251       0x01,                                               // ca pmt command
00252 #define PRIV_POS 13
00253       0x81,0x08,0x00,0x00,0xFF,0x00,0xFF,0xFF,0xFF,0xFF,  // private descr
00254 #define DMX_POS 23
00255      0x82,0x02,0xFF,0xFF,                                // demuxer stuff
00256 #define PID_POS 27
00257       0x84,0x02,0xFF,0xFF                                 // pmt pid
00258       };
00259     unsigned char *capmt=AUTOMEM(sizeof(pmt)+n+32);
00260     memcpy(capmt,pmt,sizeof(pmt));
00261     int wp=sizeof(pmt);
00262     int len=wp-LEN_POS-2;
00263     capmt[PRG_POS]=ecm->prgId>>8;
00264     capmt[PRG_POS+1]=ecm->prgId&0xff;
00265     capmt[VERS_POS]=pmtversion;
00266     pmtversion=(pmtversion+1)&0x1f;
00267     capmt[PRIV_POS+4]=cardnum;
00268     capmt[PRIV_POS+6]=ecm->transponder>>8;
00269     capmt[PRIV_POS+7]=ecm->transponder&0xFF;
00270     capmt[PRIV_POS+8]=ecm->provId>>8;
00271     capmt[PRIV_POS+9]=ecm->provId&0xFF;
00272     capmt[DMX_POS+2]=1<<cardnum ;
00273     capmt[DMX_POS+3]=cardnum ;
00274     capmt[PID_POS+2]=ecm->ecm_pid>>8;
00275     capmt[PID_POS+3]=ecm->ecm_pid&0xFF;
00276     memcpy(&capmt[wp],descr,n);
00277     len+=n; wp+=n;
00278     capmt[wp++]=0x01;
00279     capmt[wp++]=0x0f;
00280     capmt[wp++]=cardnum;   // cccam uses this one as PID to program ca0
00281     capmt[wp++]=0x00;      //es_length
00282     capmt[wp++]=0x06;      //es ca_pmt_cmd_id
00283     capmt[LEN_POS]=((len&0xf00)>>8);
00284     capmt[LEN_POS+1]=(len&0xff);
00285     capmt[4]=(wp-6)>>8;
00286     capmt[5]=(wp-6)&0xff;
00287 
00288     HEXDUMP(L_CC_CCCAM,capmt,wp,"%d: sending capmts for pid %04x",cardnum,ecm->ecm_pid);
00289     c->NewCaPmt(ecm->ecm_pid,capmt,wp);
00290     timeout=6000;
00291     }
00292   if(!c->GetCw(cw,timeout)) {
00293     PRINTF(L_CC_CCCAM,"%d: getting CW timedout, re-writing CAPMT",cardnum);
00294     // somethings up, so we will send capmt again.
00295     c->WriteCaPmt();
00296     if(!c->GetCw(cw,timeout)) {
00297       PRINTF(L_CC_CCCAM,"%d: getting CW timedout again, failed ECM",cardnum);
00298       c->Disconnect();
00299       failedcw++;
00300       if(failedcw>=10) {
00301         // CCcam is having problems lets mark it for a restart....
00302         PRINTF(L_CC_CCCAM,"%d: marked CCcam for restart",cardnum);
00303         FILE *f=fopen("/tmp/killCCcam","w+");
00304         fclose(f);
00305         failedcw=0;
00306         }
00307       return false;
00308       }
00309     }
00310   PRINTF(L_CC_CCCAM,"%d: got a CW",cardnum);
00311   failedcw=0;
00312   return true;
00313 }
00314 
00315 void cCardClientCCcam::Action(void)
00316 {
00317   unsigned char cw[18];
00318   while(Running()) {
00319     if(so.Read(cw,sizeof(cw))>0) {
00320       LDUMP(L_CC_CCCAM,cw+2,16,"got: %02x%02x ",cw[0],cw[1]);
00321       if(cw[1]==0x0f && cw[0]<4)
00322         card[cw[0]].NewCw(cw+2);
00323       }
00324     }
00325 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines