#include "globals.h" #include "reader-common.h" #include "csctapi/ifd_sc8in1.h" int32_t logfd = 0; void reader_do_idle(struct s_reader * reader); void cs_ri_brk(struct s_reader * reader, int32_t flag) { if (flag) reader->brk_pos=reader->init_history_pos; else reader->init_history_pos=reader->brk_pos; } void cs_ri_log(struct s_reader * reader, char *fmt,...) { char txt[256]; va_list params; va_start(params, fmt); vsnprintf(txt, sizeof(txt), fmt, params); va_end(params); cs_log("%s", txt); if (cfg.saveinithistory) { int32_t size = reader->init_history_pos+strlen(txt)+2; cs_realloc(&reader->init_history, size, -1); if (!reader->init_history) return; snprintf(reader->init_history+reader->init_history_pos, strlen(txt)+2, "%s\n", txt); reader->init_history_pos+=strlen(txt)+1; } } /** * add one entitlement item to entitlements of reader. **/ void cs_add_entitlement(struct s_reader *rdr, uint16_t caid, uint32_t provid, uint64_t id, uint32_t class, time_t start, time_t end, uint8_t type) { if (!rdr->ll_entitlements) rdr->ll_entitlements = ll_create("ll_entitlements"); S_ENTITLEMENT *item; if(cs_malloc(&item,sizeof(S_ENTITLEMENT), -1)){ // fill item item->caid = caid; item->provid = provid; item->id = id; item->class = class; item->start = start; item->end = end; item->type = type; //add item ll_append(rdr->ll_entitlements, item); // cs_debug_mask(D_TRACE, "entitlement: Add caid %4X id %4X %s - %s ", item->caid, item->id, item->start, item->end); } } /** * clears entitlements of reader. **/ void cs_clear_entitlement(struct s_reader *rdr) { if (!rdr->ll_entitlements) return; ll_clear_data(rdr->ll_entitlements); } void casc_check_dcw(struct s_reader * reader, int32_t idx, int32_t rc, uchar *cw) { int32_t i, pending=0; time_t t = time(NULL); ECM_REQUEST *ecm; struct s_client *cl = reader->client; if(!cl) return; for (i = 0; i < cfg.max_pending; i++) { ecm = &cl->ecmtask[i]; if ((ecm->rc>=10) && ecm->caid == cl->ecmtask[idx].caid && (!memcmp(ecm->ecmd5, cl->ecmtask[idx].ecmd5, CS_ECMSTORESIZE))) { if (rc) { //write_ecm_answer(reader, ecm->parent, (i==idx) ? E_FOUND : E_CACHE2, 0, cw, NULL); //Cache2 now generated by distribute_ecm write_ecm_answer(reader, ecm->parent, E_FOUND, 0, cw, NULL); } else write_ecm_answer(reader, ecm->parent, E_NOTFOUND, 0 , NULL, NULL); ecm->idx=0; ecm->rc=0; } if (ecm->rc>=10 && (t-(uint32_t)ecm->tps.time > ((cfg.ctimeout + 500) / 1000) + 1)) { // drop timeouts ecm->rc=0; #ifdef WITH_LB send_reader_stat(reader, ecm, NULL, E_TIMEOUT); #endif } if (ecm->rc >= 10) pending++; } cl->pending=pending; } int32_t hostResolve(struct s_reader *rdr){ struct s_client *cl = rdr->client; if(!cl) return 0; in_addr_t last_ip = cl->ip; cl->ip = cs_getIPfromHost(rdr->device); cl->udp_sa.sin_addr.s_addr = cl->ip; if (cl->ip != last_ip) { cs_log("%s: resolved ip=%s", rdr->device, cs_inet_ntoa(cl->ip)); } return cl->ip?1:0; } void clear_block_delay(struct s_reader *rdr) { rdr->tcp_block_delay = 0; cs_ftime(&rdr->tcp_block_connect_till); } void block_connect(struct s_reader *rdr) { if (!rdr->tcp_block_delay) rdr->tcp_block_delay = 100; //starting blocking time, 100ms cs_ftime(&rdr->tcp_block_connect_till); rdr->tcp_block_connect_till.time += rdr->tcp_block_delay / 1000; rdr->tcp_block_connect_till.millitm += rdr->tcp_block_delay % 1000; rdr->tcp_block_delay *= 4; //increment timeouts if (rdr->tcp_block_delay >= 60*1000) rdr->tcp_block_delay = 60*1000; //max 1min, todo config cs_debug_mask(D_TRACE, "tcp connect blocking delay for %s set to %d", rdr->label, rdr->tcp_block_delay); } int32_t is_connect_blocked(struct s_reader *rdr) { struct timeb cur_time; cs_ftime(&cur_time); int32_t blocked = (rdr->tcp_block_delay && comp_timeb(&cur_time, &rdr->tcp_block_connect_till) < 0); if (blocked) { int32_t time = 1000*(rdr->tcp_block_connect_till.time-cur_time.time) +rdr->tcp_block_connect_till.millitm-cur_time.millitm; cs_log("%s connection blocked, retrying in %ds", rdr->label, time/1000); } return blocked; } int32_t network_tcp_connection_open(struct s_reader *rdr) { if (!rdr) return -1; struct s_client *client = rdr->client; struct sockaddr_in loc_sa; memset((char *)&client->udp_sa, 0, sizeof(client->udp_sa)); in_addr_t last_ip = client->ip; if (!hostResolve(rdr)) return -1; if (last_ip != client->ip) //clean blocking delay on ip change: clear_block_delay(rdr); if (is_connect_blocked(rdr)) { //inside of blocking delay, do not connect! return -1; } if (client->reader->r_port<=0) { cs_log("invalid port %d for server %s", client->reader->r_port, client->reader->device); return -1; } client->is_udp=(rdr->typ==R_CAMD35); cs_log("connecting to %s on %s:%d", rdr->label, rdr->device, rdr->r_port); if (client->udp_fd) cs_log("WARNING: client->udp_fd was not 0"); if ((client->udp_fd=socket(PF_INET, client->is_udp ? SOCK_DGRAM : SOCK_STREAM, client->is_udp ? IPPROTO_UDP : IPPROTO_TCP))<0) { cs_log("Socket creation failed (errno=%d %s)", errno, strerror(errno)); client->udp_fd = 0; block_connect(rdr); return -1; } set_socket_priority(client->udp_fd, cfg.netprio); int32_t keep_alive = 1; setsockopt(client->udp_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keep_alive, sizeof(keep_alive)); int32_t flag = 1; setsockopt(client->udp_fd, IPPROTO_TCP, TCP_NODELAY, (void *)&flag, sizeof(flag)); if (client->reader->l_port>0) { memset((char *)&loc_sa,0,sizeof(loc_sa)); loc_sa.sin_family = AF_INET; if (cfg.srvip) loc_sa.sin_addr.s_addr = cfg.srvip; else loc_sa.sin_addr.s_addr = INADDR_ANY; loc_sa.sin_port = htons(client->reader->l_port); if (bind(client->udp_fd, (struct sockaddr *)&loc_sa, sizeof (loc_sa))<0) { cs_log("bind failed (errno=%d %s)", errno, strerror(errno)); close(client->udp_fd); client->udp_fd = 0; block_connect(rdr); return -1; } } client->udp_sa.sin_family = AF_INET; client->udp_sa.sin_port = htons((uint16_t)client->reader->r_port); cs_log("socket open for %s fd=%d", rdr->ph.desc, client->udp_fd); if (client->is_udp) { rdr->tcp_connected = 1; return client->udp_fd; } int32_t fl = fcntl(client->udp_fd, F_GETFL); fcntl(client->udp_fd, F_SETFL, O_NONBLOCK); int32_t res = connect(client->udp_fd, (struct sockaddr *)&client->udp_sa, sizeof(client->udp_sa)); if (res == -1) { int32_t r = -1; if (errno == EINPROGRESS || errno == EALREADY) { struct pollfd pfd; pfd.fd = client->udp_fd; pfd.events = POLLOUT; int32_t rc = poll(&pfd, 1, 3000); if (rc > 0) { uint32_t l = sizeof(r); if (getsockopt(client->udp_fd, SOL_SOCKET, SO_ERROR, &r, (socklen_t*)&l) != 0) r = -1; else errno = r; } else { errno = ETIMEDOUT; } } if (r != 0) { cs_log("connect(fd=%d) failed: (errno=%d %s)", client->udp_fd, errno, strerror(errno)); block_connect(rdr); //connect has failed. Block connect for a while close(client->udp_fd); client->udp_fd = 0; return -1; } } fcntl(client->udp_fd, F_SETFL, fl); //restore blocking mode setTCPTimeouts(client->udp_fd); clear_block_delay(rdr); client->last=client->login=time((time_t*)0); client->last_caid=client->last_srvid=0; client->pfd = client->udp_fd; rdr->tcp_connected = 1; cs_log("connect succesfull %s fd=%d", rdr->ph.desc, client->udp_fd); return client->udp_fd; } void network_tcp_connection_close(struct s_reader *reader, char *reason) { if (!reader) { //only proxy reader should call this, client connections are closed on thread cleanup cs_log("WARNING: invalid client tcp_conn_close()"); cs_disconnect_client(cur_client()); return; } struct s_client *cl = reader->client; if(!cl) return; int32_t fd = cl->udp_fd; int32_t i; if (fd) { cs_log("tcp_conn_close(): fd=%d, cl->typ == '%c' is_udp %d label == '%s' reason %s", fd, cl->typ, cl->is_udp, reader->label, reason?reason:"undef"); close(fd); cl->udp_fd = 0; cl->pfd = 0; } reader->tcp_connected = 0; reader->card_status = UNKNOWN; if (cl->ecmtask) { for (i = 0; i < cfg.max_pending; i++) { cl->ecmtask[i].idx = 0; cl->ecmtask[i].rc = 0; } } } void casc_do_sock_log(struct s_reader * reader) { int32_t i, idx; uint16_t caid, srvid; uint32_t provid; struct s_client *cl = reader->client; if(!cl) return; idx=reader->ph.c_recv_log(&caid, &provid, &srvid); cl->last=time((time_t*)0); if (idx<0) return; // no dcw-msg received if(!cl->ecmtask) { cs_log("WARNING: casc_do_sock_log: ecmtask not a available"); return; } for (i = 0; i < cfg.max_pending; i++) { if ( (cl->ecmtask[i].rc>=10) && (cl->ecmtask[i].idx==idx) && (cl->ecmtask[i].caid==caid) && (cl->ecmtask[i].prid==provid) && (cl->ecmtask[i].srvid==srvid)) { casc_check_dcw(reader, i, 0, cl->ecmtask[i].cw); // send "not found" break; } } } int32_t casc_process_ecm(struct s_reader * reader, ECM_REQUEST *er) { int32_t rc, n, i, sflag, pending=0; time_t t;//, tls; struct s_client *cl = reader->client; if(!cl || !cl->ecmtask) { cs_log("WARNING: casc_process_ecm: ecmtask not a available"); return -1; } uchar buf[512]; t=time((time_t *)0); ECM_REQUEST *ecm; for (i = 0; i < cfg.max_pending; i++) { ecm = &cl->ecmtask[i]; if ((ecm->rc>=10) && (t-(uint32_t)ecm->tps.time > ((cfg.ctimeout + 500) / 1000) + 1)) { // drop timeouts ecm->rc=0; #ifdef WITH_LB send_reader_stat(reader, ecm, NULL, E_TIMEOUT); #endif } } for (n = -1, i = 0, sflag = 1; i < cfg.max_pending; i++) { ecm = &cl->ecmtask[i]; if (n<0 && (ecm->rc<10)) // free slot found n=i; // ecm already pending // ... this level at least if ((ecm->rc>=10) && er->caid == ecm->caid && (!memcmp(er->ecmd5, ecm->ecmd5, CS_ECMSTORESIZE)) && (er->level<=ecm->level)) sflag=0; if (ecm->rc >=10) pending++; } cl->pending=pending; if (n<0) { cs_log("WARNING: reader ecm pending table overflow !!"); return(-2); } memcpy(&cl->ecmtask[n], er, sizeof(ECM_REQUEST)); cl->ecmtask[n].matching_rdr = NULL; //This avoids double free of matching_rdr! #ifdef CS_CACHEEX cl->ecmtask[n].csp_lastnodes = NULL; //This avoids double free of csp_lastnodes! #endif cl->ecmtask[n].parent = er; if( reader->typ == R_NEWCAMD ) cl->ecmtask[n].idx=(cl->ncd_msgid==0)?2:cl->ncd_msgid+1; else { if (!cl->idx) cl->idx = 1; cl->ecmtask[n].idx=cl->idx++; } cl->ecmtask[n].rc=10; cs_debug_mask(D_TRACE, "---- ecm_task %d, idx %d, sflag=%d, level=%d", n, cl->ecmtask[n].idx, sflag, er->level); cs_ddump_mask(D_ATR, er->ecm, er->l, "casc ecm:"); rc=0; if (sflag) { if ((rc=reader->ph.c_send_ecm(cl, &cl->ecmtask[n], buf))) casc_check_dcw(reader, n, 0, cl->ecmtask[n].cw); // simulate "not found" else cl->last_idx = cl->ecmtask[n].idx; reader->last_s = t; // used for inactive_timeout and reconnect_timeout in TCP reader } if (cl->idx>0x1ffe) cl->idx=1; return(rc); } static int32_t reader_store_emm(uchar type, uchar *emmd5) { int32_t rc; struct s_client *cl = cur_client(); memcpy(cl->emmcache[cl->rotate].emmd5, emmd5, CS_EMMSTORESIZE); cl->emmcache[cl->rotate].type=type; cl->emmcache[cl->rotate].count=1; // cs_debug_mask(D_READER, "EMM stored (index %d)", rotate); rc=cl->rotate; cl->rotate=(++cl->rotate < CS_EMMCACHESIZE)?cl->rotate:0; return(rc); } void reader_get_ecm(struct s_reader * reader, ECM_REQUEST *er) { struct s_client *cl = reader->client; if(!cl) return; if (er->rc<=E_STOPPED) { //TODO: not sure what this is for, but it was in mpcs too. // ecm request was already answered when the request was started (this ECM_REQUEST is a copy of client->ecmtask[] ECM_REQUEST). // send_dcw is a client function but reader_get_ecm is only called from reader functions where client->ctyp is not set and so send_dcw() will segfault. // so we could use send_dcw(er->client, er) or write_ecm_answer(reader, er), but send_dcw wont be threadsafe from here cause there may be multiple threads accessing same s_client struct. // maybe rc should be checked before request is sent to reader but i could not find the reason why this is happening now and not in v1.10 (zetack) //send_dcw(cl, er); cs_debug_mask(D_TRACE, "skip ecm %04X reader=%s, rc=%d", er->checksum, reader->label, er->rc); return; } if (!chk_bcaid(er, &reader->ctab)) { cs_debug_mask(D_READER, "caid %04X filtered", er->caid); write_ecm_answer(reader, er, E_NOTFOUND, E2_CAID, NULL, NULL); return; } // cache2 struct ecm_request_t *ecm = check_cwcache(er, cl); if (ecm && ecm->rc <= E_NOTFOUND) { cs_debug_mask(D_TRACE, "ecm %04X answer from cache reader=%s", er->checksum, reader->label); write_ecm_answer(reader, er, E_CACHE2, 0, ecm->cw, NULL); return; } if (reader->typ & R_IS_CASCADING) { cl->last_srvid=er->srvid; cl->last_caid=er->caid; casc_process_ecm(reader, er); cl->lastecm=time((time_t*)0); return; } #ifdef WITH_CARDREADER if (reader->cooldown[0] && reader->ratelimitecm){ if (!reader->cooldowntime) reader->cooldowntime = time((time_t*)0); time_t now = time((time_t*)0); if (reader->cooldownstate == 1) { if (now - reader->cooldowntime >= reader->cooldown[1]) { reader->cooldownstate = 0; reader->cooldowntime = now; cs_log("%s cooldown OFF", reader->label); } } else { if (now - reader->cooldowntime >= reader->cooldown[0]) { reader->cooldownstate = 1; reader->cooldowntime = now; cs_log("%s cooldown ON", reader->label); } } } if ((reader->ratelimitecm && !reader->cooldown[0]) || reader->cooldownstate == 1 ) { cs_debug_mask(D_READER, "ratelimit idx:%d rc:%d caid:%04X srvid:%04X",er->idx,er->rc,er->caid,er->srvid); int32_t foundspace=-1; int32_t h; for (h=0;hratelimitecm;h++) { if (reader->rlecmh[h].srvid == er->srvid) { foundspace=h; cs_debug_mask(D_READER, "ratelimit found srvid in use at pos: %d",h); break; } } if (foundspace<0) { for (h=0;hratelimitecm;h++) { if ((reader->rlecmh[h].last ==- 1) || ((time(NULL)-reader->rlecmh[h].last) > reader->ratelimitseconds)) { foundspace=h; cs_debug_mask(D_READER, "ratelimit found space at pos: %d old seconds %ld",h,reader->rlecmh[h].last); break; } } } #ifdef HAVE_DVBAPI //overide ratelimit priority for dvbapi request if ((foundspace < 0) && (cfg.dvbapi_enabled == 1) && (strcmp(er->client->account->usr,cfg.dvbapi_usr) == 0)) { if(reader->lastdvbapirateoverride < time(NULL) - reader->ratelimitseconds){ time_t minecmtime = time(NULL); for (h=0;hratelimitecm;h++) { if(reader->rlecmh[h].last < minecmtime){ foundspace = h; minecmtime = reader->rlecmh[h].last; } } reader->lastdvbapirateoverride = time(NULL); cs_debug_mask(D_READER, "Prioritizing DVBAPI User %s over other watching client on reader %s.",er->client->account->usr, reader->label); } else cs_debug_mask(D_READER, "DVBAPI User %s is switching too fast for ratelimit and can't be prioritized on reader %s.",er->client->account->usr, reader->label); } #endif if (foundspace<0) { //drop cs_debug_mask(D_READER, "ratelimit could not find space for srvid %04X. Dropping.",er->srvid); write_ecm_answer(reader, er, E_NOTFOUND, E2_RATELIMIT, NULL, "ECMratelimit no space for srvid"); return; } else { reader->rlecmh[foundspace].last=time(NULL); reader->rlecmh[foundspace].srvid=er->srvid; } } cs_ddump_mask(D_ATR, er->ecm, er->l, "ecm:"); struct timeb tps, tpe; cs_ftime(&tps); struct s_ecm_answer ea; memset(&ea, 0, sizeof(struct s_ecm_answer)); int32_t rc = reader_ecm(reader, er, &ea); ea.rc = E_FOUND; //default assume found ea.rcEx = 0; //no special flag if(rc == ERROR ){ char buf[32]; cs_debug_mask(D_TRACE, "Error processing ecm for caid %04X, srvid %04X (servicename: %s) on reader %s.", er->caid, er->srvid, get_servicename(cl, er->srvid, er->caid, buf), reader->label); ea.rc = E_NOTFOUND; ea.rcEx = 0; if (reader->typ == R_SC8in1 && reader->sc8in1_config->mcr_type) { char text[] = {'S', (char)reader->slot+0x30, 'E', 'e', 'r'}; MCR_DisplayText(reader, text, 5, 400, 0); } } if(rc == E_CORRUPT ){ char buf[32]; cs_debug_mask(D_TRACE, "Error processing ecm for caid %04X, srvid %04X (servicename: %s) on reader %s.", er->caid, er->srvid, get_servicename(cl, er->srvid, er->caid, buf), reader->label); ea.rc = E_NOTFOUND; ea.rcEx = E2_WRONG_CHKSUM; //flag it as wrong checksum memcpy (ea.msglog,"Invalid ecm type for card",25); } cs_ftime(&tpe); cl->lastecm=time((time_t*)0); cs_debug_mask(D_TRACE, "reader: %s ecm: %04X real time: %ld ms", reader->label, htons(er->checksum), 1000*(tpe.time-tps.time)+tpe.millitm-tps.millitm); write_ecm_answer(reader, er, ea.rc, ea.rcEx, ea.cw, ea.msglog); reader_post_process(reader); #endif } void reader_log_emm(struct s_reader * reader, EMM_PACKET *ep, int32_t i, int32_t rc, struct timeb *tps) { char *rtxt[] = { "error", (reader->typ & R_IS_CASCADING) ? "sent" : "written", "skipped", "blocked" }; char *typedesc[] = { "unknown", "unique", "shared", "global" }; struct s_client *cl = reader->client; struct timeb tpe; if (reader->logemm & (1 << rc)) { cs_ftime(&tpe); if (!tps) tps = &tpe; cs_log("%s emmtype=%s, len=%d, idx=%d, cnt=%d: %s (%ld ms) by %s", username(ep->client), typedesc[cl->emmcache[i].type], ep->emm[2], i, cl->emmcache[i].count, rtxt[rc], 1000*(tpe.time-tps->time)+tpe.millitm-tps->millitm, reader->label); } if (rc) cl->lastemm = time((time_t*) 0); #if defined(__ARM__) if (rc && cfg.enableled == 1) cs_switch_led(LED3, LED_BLINK_ON); #endif #if defined(WEBIF) || defined(LCDSUPPORT) //counting results switch (rc) { case 0: reader->emmerror[ep->type]++; break; case 1: reader->emmwritten[ep->type]++; break; case 2: reader->emmskipped[ep->type]++; break; case 3: reader->emmblocked[ep->type]++; break; } #endif #ifdef QBOXHD if (rc && cfg.enableled == 2) qboxhd_led_blink(QBOXHD_LED_COLOR_BLUE,QBOXHD_LED_BLINK_MEDIUM); #endif } int32_t reader_do_emm(struct s_reader * reader, EMM_PACKET *ep) { int32_t i, rc, ecs; unsigned char md5tmp[MD5_DIGEST_LENGTH]; struct timeb tps; struct s_client *cl = reader->client; if(!cl) return 0; cs_ftime(&tps); MD5(ep->emm, ep->emm[2], md5tmp); for (i=ecs=0; iemmcache[i].emmd5, md5tmp, CS_EMMSTORESIZE)) { cl->emmcache[i].count++; if (reader->cachemm){ if (cl->emmcache[i].count > reader->rewritemm){ ecs=2; //skip emm } else ecs=1; //rewrite emm } break; } } //Ecs=0 not found in cache //Ecs=1 found in cache, rewrite emm //Ecs=2 skip if ((rc=ecs)<2) { if (reader->typ & R_IS_CASCADING) { cs_debug_mask(D_READER, "network emm reader: %s" ,reader->label); if (reader->ph.c_send_emm) { rc=reader->ph.c_send_emm(ep); } else { cs_debug_mask(D_READER, "send_emm() support missing"); rc=0; } } else { cs_debug_mask(D_READER, "local emm reader: %s" ,reader->label); #ifdef WITH_CARDREADER rc=reader_emm(reader, ep); #else rc=0; #endif } if (!ecs) i=reader_store_emm(ep->type, md5tmp); } reader_log_emm(reader, ep, i, rc, &tps); return(rc); } void reader_do_card_info(struct s_reader * reader) { #ifdef WITH_CARDREADER reader_card_info(reader); #endif if (reader->ph.c_card_info) reader->ph.c_card_info(); } void reader_do_idle(struct s_reader * reader) { if (reader->ph.c_idle) reader->ph.c_idle(); else { time_t now; int32_t time_diff; time(&now); time_diff = abs(now - reader->last_s); if (time_diff>(reader->tcp_ito*60)) { struct s_client *cl = reader->client; if (cl && reader->tcp_connected && reader->ph.type==MOD_CONN_TCP) { cs_debug_mask(D_READER, "%s inactive_timeout, close connection (fd=%d)", reader->ph.desc, cl->pfd); network_tcp_connection_close(reader, "inactivity"); } else reader->last_s = now; } } } int32_t reader_init(struct s_reader *reader) { struct s_client *client = reader->client; if (reader->typ & R_IS_CASCADING) { client->typ='p'; client->port=reader->r_port; client->ip=cs_inet_addr("0.0.0.0"); if (!(reader->ph.c_init)) { cs_log("FATAL: %s-protocol not supporting cascading", reader->ph.desc); return 0; } if (reader->ph.c_init(client)) { //proxy reader start failed return 0; } if ((reader->log_port) && (reader->ph.c_init_log)) reader->ph.c_init_log(); cs_malloc(&client->ecmtask, cfg.max_pending * sizeof(ECM_REQUEST), 1); cs_log("proxy %s initialized (server=%s:%d)", reader->label, reader->device, reader->r_port); } #ifdef WITH_CARDREADER else { client->typ='r'; client->ip=cs_inet_addr("127.0.0.1"); while (reader_device_init(reader)==2){ int8_t i = 0; do{ cs_sleepms(2000); if(!ll_contains(configured_readers, reader) || !check_client(client) || reader->enable != 1) return 0; ++i; } while (i < 30); } if (reader->mhz > 2000) { int32_t divider = 0; double cardclock1, cardclock2; while (divider != reader->mhz/100){ //calculate PLL divider ugly fast fix should be global function-> FIX ME!!!! divider++; cardclock1 = reader->mhz / divider; divider++; cardclock2 = reader->mhz / (divider); if ((cardclock1 > reader->cardmhz) && (cardclock2 > reader->cardmhz)) continue; if ( abs(cardclock1 - reader->cardmhz) > abs(cardclock2 - reader->cardmhz) ) break; divider--; break; } cs_log("Reader %s initialized (device=%s, detect=%s%s, pll max=%.2f Mhz, divider=%d, actual cardmhz=%.2f Mhz", reader->label, reader->device, reader->detect&0x80 ? "!" : "",RDR_CD_TXT[reader->detect&0x7f], (float) (reader->mhz /100), divider, (float) (reader->mhz / divider)/100L); } else { cs_log("reader %s initialized (device=%s, detect=%s%s, mhz=%d, cardmhz=%d)", reader->label, reader->device, reader->detect&0x80 ? "!" : "",RDR_CD_TXT[reader->detect&0x7f], reader->mhz,reader->cardmhz); } } #endif cs_malloc(&client->emmcache,CS_EMMCACHESIZE*(sizeof(struct s_emm)), 1); client->login=time((time_t*)0); client->init_done=1; return 1; } #if !defined(WITH_CARDREADER) && defined(WITH_STAPI) /* Dummy function stub for stapi compiles without cardreader as libstapi needs it. */ int32_t ATR_InitFromArray (ATR * atr, const BYTE atr_buffer[ATR_MAX_SIZE], uint32_t length){ return 0; } #endif