|
SASC-NG
DVBSoftCAM
|
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 }