[gbx] - try fix ecm and cw messages
[oscam.git] / reader-seca.c
blob1bbf1a289369a2f3ae1e20c2254c1743288c5991
1 #include "globals.h"
2 #ifdef READER_SECA
3 #include "reader-common.h"
4 #include "csctapi/icc_async.h"
5 #include "cscrypt/idea.h"
7 struct seca_data
9 bool valid_provider[CS_MAXPROV];
10 IDEA_KEY_SCHEDULE ks;
11 IDEA_KEY_SCHEDULE ksSession;
15 static uint64_t get_pbm(struct s_reader *reader, uint8_t idx, bool fedc)
17 def_resp;
18 uint8_t ins34[] = { 0xc1, 0x34, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00 }; // set request options
19 uint8_t ins32[] = { 0xc1, 0x32, 0x00, 0x00, 0x0A }; // get PBM
20 uint64_t pbm = 0;
22 ins32[2] = idx;
23 if (!idx) // change request options for first (=managment) provider only
25 ins32[4] = 0x0D;
26 ins34[5] = 0x04;
29 if(!fedc)
31 write_cmd(ins34, ins34 + 5); // set request options
32 write_cmd(ins32, NULL); // pbm request
34 switch(cta_res[0])
36 case 0x04:
37 rdr_log(reader, "no PBM for provider %u", idx + 1);
38 break;
40 case 0x83:
41 pbm = b2ll(8, cta_res + 1);
42 rdr_log(reader, "PBM for provider %u: %08llx", idx + 1, (unsigned long long) pbm);
43 break;
45 case 0xb2:
46 pbm = b2ll(8, cta_res + 1);
47 rdr_log(reader, "PBM for provider %u: %08llx", idx + 1, (unsigned long long) pbm);
48 break;
50 default:
51 rdr_log(reader, "ERROR: PBM returns unknown byte %02x", cta_res[0]);
54 return pbm;
57 static int32_t set_provider_info(struct s_reader *reader, int32_t i)
59 def_resp;
60 uint8_t ins12[] = { 0xc1, 0x12, 0x00, 0x00, 0x19 }; // get provider info
61 int32_t year, month, day;
62 struct tm lt;
63 time_t t;
64 bool valid = false;
65 bool fedc = false;
66 char l_name[16 + 8 + 1] = ", name: ";
67 char tmp[9];
69 uint32_t provid;
71 ins12[2] = i; // select provider
72 rdr_log(reader, "Request provider %i", i + 1);
73 write_cmd(ins12, NULL); // show provider properties
75 if((cta_res[25] != 0x90) || (cta_res[26] != 0x00))
77 return ERROR;
80 reader->prid[i][0] = 0;
81 reader->prid[i][1] = 0; // blanken high byte provider code
83 if (cta_res[0] == 0xFE)
85 fedc = true;
86 rdr_log(reader, "FEDC provider %i", i + 1);
87 cta_res[0] = 0x00;
89 switch(i + 1)
91 case 0x01:
92 cta_res[1] = 0x00;
93 break;
95 case 0x02:
96 cta_res[1] = 0x68;
97 break;
99 case 0x03:
100 cta_res[1] = 0x65;
101 break;
103 default:
104 cta_res[1] = 0x68;
107 memcpy(&reader->prid[i][2], cta_res, 2);
109 provid = b2ll(4, reader->prid[i]);
110 year = (cta_res[22] >> 1) + 1990;
111 month = ((cta_res[22] & 0x1) << 3) | (cta_res[23] >> 5);
112 day = (cta_res[23] & 0x1f);
113 t = time(NULL);
114 localtime_r(&t, &lt);
116 if(lt.tm_year + 1900 != year)
118 valid = (lt.tm_year + 1900 < year);
120 else if(lt.tm_mon + 1 != month)
122 valid = (lt.tm_mon + 1 < month);
124 else if(lt.tm_mday != day)
126 valid = (lt.tm_mday < day);
129 memcpy(l_name + 8, cta_res + 2, 16);
130 l_name[sizeof(l_name) - 1] = 0;
131 trim(l_name + 8);
132 l_name[0] = (l_name[8]) ? ',' : 0;
134 if(l_name[8])
136 add_provider(0x0100, provid, l_name + 8, "", "");
139 struct seca_data *csystem_data = reader->csystem_data;
140 csystem_data->valid_provider[i] = valid;
141 rdr_log(reader, "provider %d: %04X, valid: %i%s, expiry date: %4d/%02d/%02d", i + 1, provid, valid, l_name, year, month, day);
142 memcpy(&reader->sa[i][0], cta_res + 18, 4);
144 if(valid) // if not expired
146 rdr_log_sensitive(reader, "SA: {%s}", cs_hexdump(0, cta_res + 18, 4, tmp, sizeof(tmp)));
149 // add entitlement to list
150 memset(&lt, 0, sizeof(struct tm));
151 lt.tm_year = year - 1900;
152 lt.tm_mon = month - 1;
153 lt.tm_mday = day;
155 // Check if entitlement entry exists
156 LL_ITER it = ll_iter_create(reader->ll_entitlements);
157 S_ENTITLEMENT *entry = NULL;
161 entry = ll_iter_next(&it);
162 if((entry) && (entry->provid == provid))
164 break;
167 while(entry);
169 if(entry)
171 // update entitlement info if found
172 entry->end = mktime(&lt);
173 entry->id = get_pbm(reader, i, fedc);
174 entry->type = (i) ? 6 : 7;
176 else // add entitlement info
178 cs_add_entitlement(reader, reader->caid, provid, get_pbm(reader, i, fedc), 0, 0, mktime(&lt), (i) ? 6 : 7, 1);
181 return OK;
184 static int32_t get_maturity(struct s_reader *reader)
186 // Get maturity on card
187 static const uint8_t ins16[] = { 0xC1, 0x16, 0x00, 0x00, 0x06 };
189 def_resp;
191 write_cmd(ins16, NULL);
192 if((cta_res[cta_lr - 2] == 0x90) && cta_res[cta_lr - 1] == 0x00)
194 reader->maturity=cta_res[cta_lr - 4] & 0xF ;
195 //rdr_log(reader, "Maturity rating on the card 0x%X!", reader->maturity);
197 if (reader->maturity<0xF)
199 rdr_log(reader, "Maturity level [%X]= older than %i years", reader->maturity, reader->maturity);
201 else
203 rdr_log(reader, "Maturity level [%X]=no age limit", reader->maturity);
207 rdr_log_dbg(reader, D_READER, "ins30_answer: %02x%02x", cta_res[0], cta_res[1]);
208 return 0;
211 static int32_t unlock_parental(struct s_reader *reader)
213 // Unlock parental control
214 // c1 30 00 01 09
215 // 00 00 00 00 00 00 00 00 ff
216 static const uint8_t ins30[] = { 0xc1, 0x30, 0x00, 0x01, 0x09 };
217 static uint8_t ins30data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF };
219 def_resp;
221 if(strcmp(reader->pincode, "none"))
223 rdr_log(reader, "Using PIN %s", reader->pincode);
224 // the pin need to be coded in bcd, so we need to convert from ascii to bcd, so '1234' -> 0x12 0x34
225 ins30data[6] = ((reader->pincode[0] - 0x30) << 4) | ((reader->pincode[1] - 0x30) & 0x0f);
226 ins30data[7] = ((reader->pincode[2] - 0x30) << 4) | ((reader->pincode[3] - 0x30) & 0x0f);
228 else
230 rdr_log(reader, "Using PIN 0000!");
233 write_cmd(ins30, ins30data);
234 rdr_log_dbg(reader, D_READER, "ins30_answer: %02x%02x", cta_res[0], cta_res[1]);
236 if(!(cta_res[cta_lr - 2] == 0x90 && cta_res[cta_lr - 1] == 0))
238 if(strcmp(reader->pincode, "none"))
240 rdr_log(reader, "Can't disable parental lock. Wrong PIN? OSCam used %s!", reader->pincode);
242 else
244 rdr_log(reader, "Can't disable parental lock. Wrong PIN? OSCam used 0000!");
247 else
249 rdr_log(reader, "Parental lock disabled");
250 get_maturity(reader);
253 return 0;
256 static int32_t seca_card_init(struct s_reader *reader, ATR *newatr)
258 get_atr;
259 def_resp;
260 char *card;
261 uint64_t serial ;
262 static const uint8_t ins0e[] = { 0xc1, 0x0e, 0x00, 0x00, 0x08 }; // get serial number (UA)
264 cs_clear_entitlement(reader);
266 if((atr[10] != 0x0e) || (atr[11] != 0x6c) || (atr[12] != 0xb6) || (atr[13] != 0xd6))
268 return ERROR;
271 if(!cs_malloc(&reader->csystem_data, sizeof(struct seca_data)))
273 return ERROR;
276 switch(atr[7] << 8 | atr[8])
278 case 0x5084:
279 card = "Generic";
280 break;
282 case 0x5384:
283 card = "Philips";
284 break;
286 case 0x5130:
287 case 0x5430:
288 case 0x5760:
289 card = "Thompson";
290 break;
292 case 0x5284:
293 case 0x5842:
294 case 0x6060:
295 card = "Siemens";
296 break;
298 case 0x7070:
299 card = "Mediaguard";
300 break;
302 default:
303 card = "Unknown";
304 break;
307 reader->caid = 0x0100;
308 memset(reader->prid, 0xff, sizeof(reader->prid));
309 write_cmd(ins0e, NULL); // read unique id
310 memcpy(reader->hexserial, cta_res + 2, 6);
311 serial = b2ll(5, cta_res + 3);
313 rdr_log_sensitive(reader, "type: SECA, caid: %04X, serial: {%llu}, card: %s v%d.%d",
314 reader->caid, (unsigned long long) serial, card, atr[9] & 0x0F, atr[9] >> 4);
316 int seca_version = atr[9] & 0X0F; // Get seca cardversion from cardatr
318 if(seca_version == 10) // check for nagra smartcard (seca3)
320 reader->secatype = 3;
321 rdr_log_dbg(reader, D_IFD, "Detected seca/nagra (seca3) card");
324 if(seca_version == 7) // check for seca smartcard (seca2)
326 reader->secatype = 2;
327 rdr_log(reader, "Detected seca2 card");
330 get_maturity(reader);
332 // Unlock parental control
333 if(cfg.ulparent != 0)
335 unlock_parental(reader);
336 get_maturity(reader);
338 else
340 rdr_log_dbg(reader, D_IFD, "parental locked");
343 struct seca_data *csystem_data = reader->csystem_data;
344 //init ideakeys
345 uint8_t IdeaKey[16];
346 memcpy(IdeaKey, reader->boxkey, 16);
347 idea_set_encrypt_key(IdeaKey, &csystem_data->ks);
348 idea_set_decrypt_key(&csystem_data->ks, &csystem_data->ksSession);
350 return OK;
353 // returns provider id or -1 if not found
354 static int32_t get_prov_index(struct s_reader *rdr, const uint8_t *provid)
356 int32_t prov;
357 for(prov = 0; prov < rdr->nprov; prov++) // search for provider index
359 if(!memcmp(provid, &rdr->prid[prov][2], 2))
361 return (prov);
364 return (-1);
367 // CDS seca2/3 solution
368 static int32_t seca_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea)
370 // provid006A=CDS NL uses seca2 and nagra/mediaguard3 crypt on same caid/provid only ecmpid is different
371 if(er->ecm[3] == 0x00 && er->ecm[4] == 0x6a)
373 // default assume ecmtype same as cardtype in reader
374 int32_t ecm_type = reader->secatype;
376 if(er->ecm[8] == 0x00) // this is a mediaguard3 ecm request
378 ecm_type = 3; // flag it!
381 if((er->ecm[8] == 0x10) && (er->ecm[9] == 0x01)) // this is a seca2 ecm request
383 ecm_type = 2; // flag it!
386 if(ecm_type != reader->secatype) // only accept ecmrequest for right card!
388 return ERROR;
392 def_resp;
393 uint8_t ins3c[] = { 0xc1, 0x3c, 0x00, 0x00, 0x00 }; // coding cw
394 uint8_t ins3a[] = { 0xc1, 0x3a, 0x00, 0x00, 0x10 }; // decoding cw
395 int32_t i;
397 if((i = get_prov_index(reader, er->ecm + 3)) == -1) // if provider not found
399 snprintf(ea->msglog, MSGLOGSIZE, "provider not found");
400 return ERROR;
403 struct seca_data *csystem_data = reader->csystem_data;
404 if((er->ecm[7] & 0x0F) != 0x0E && !csystem_data->valid_provider[i]) // if expired and not using OP Key 0E
406 snprintf(ea->msglog, MSGLOGSIZE, "provider expired");
407 return ERROR;
410 ins3c[2] = i;
411 ins3c[3] = er->ecm[7]; // key nr
412 ins3c[4] = (((er->ecm[1] & 0x0f) << 8) | er->ecm[2]) - 0x05;
413 int32_t try = 1;
414 int32_t ret;
418 if(try > 1)
420 snprintf(ea->msglog, MSGLOGSIZE, "ins3c try nr %i", try);
423 write_cmd(ins3c, er->ecm + 8); // ecm request
424 uint8_t ins30[] = { 0xC1, 0x30, 0x00, 0x02, 0x09 };
425 uint8_t ins30data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF };
427 /* We need to use a token */
428 if(cta_res[0] == 0x90 && cta_res[1] == 0x1a)
430 write_cmd(ins30, ins30data);
431 write_cmd(ins3c, er->ecm + 8); // ecm request
434 ret = (((cta_res[0] != 0x90) && (cta_res[0] != 0x93) && (cta_res[0] != 0x96)) || ((cta_res[1] != 0x00) && (cta_res[1] != 0x02)));
436 // Handle all not initial 90 00 ecm of with a get decoding cw
437 // does avoid the need off card reset after a lot off them
438 // the try ++ has been removed as it triggers the anti share mode
439 // off seca cards due to not recorded extra ecm's by rate limiter
441 if((cta_res[0] == 0x93) && (cta_res[1] == 0x02))
443 write_cmd(ins3a, NULL); // get cw
444 snprintf(ea->msglog, MSGLOGSIZE, "unsubscribed 93 02");
445 return ERROR;
446 } // exit if unsubscribed
448 if((cta_res[0] == 0x96) && (cta_res[1] == 0x00))
450 write_cmd(ins3a, NULL); // get cw
451 snprintf(ea->msglog, MSGLOGSIZE, "fake 96 00 ecm");
452 return E_CORRUPT;
453 } // exit if fake 96 00 ecm
455 if(ret)
457 snprintf(ea->msglog, MSGLOGSIZE, "%.16s ins3c card res: %02x %02x", reader->label, cta_res[0] , cta_res[1]);
458 write_cmd(ins3a, NULL); // get cw
459 return ERROR;
460 } // exit on other's then 96 00 or 93 02
463 while((try < 2) && (ret));
465 if(ret)
467 return ERROR;
470 write_cmd(ins3a, NULL); // get cw's
471 if((cta_res[16] != 0x90) || (cta_res[17] != 0x00))
473 snprintf(ea->msglog, MSGLOGSIZE, "ins3a card response: %02x %02x", cta_res[16] , cta_res[17]);
474 return ERROR;
475 } // exit if response not 90 00
477 // TODO: if response is 9027 ppv mode is possible!
479 if(er->ecm[5] == 0x01 && ((reader->card_atr[9] & 0X0F) == 10)) // seca3: nano 01 in effect?
481 if(reader->boxkey_length == 16)
483 uint8_t v[8];
484 memset(v, 0, sizeof(v));
485 idea_cbc_encrypt(cta_res, ea->cw, 8, &csystem_data->ksSession, v, IDEA_DECRYPT);
486 memset(v, 0, sizeof(v));
487 idea_cbc_encrypt(cta_res + 8, ea->cw + 8, 8, &csystem_data->ksSession, v, IDEA_DECRYPT);
488 uint8_t c;
490 for(i = 0; i < 16; i += 4)
492 c = ((ea->cw[i] + ea->cw[i + 1] + ea->cw[i + 2]) & 0xff);
494 if(ea->cw[i + 3] != c)
496 break;
500 if(i == 16)
502 return OK;
505 memset(ea->cw, 0, 16);
506 snprintf(ea->msglog, MSGLOGSIZE, "need sessionkey");
507 return ERROR;
509 memcpy(ea->cw, cta_res, 16);
510 return OK;
513 // returns 1 if shared emm matches SA, unique emm matches serial, or global or unknown
514 static int32_t seca_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr)
516 rdr_log_dbg(rdr, D_EMM, "Entered seca_get_emm_type ep->emm[0]=%i", ep->emm[0]);
517 int32_t i;
518 char tmp_dbg[25];
520 switch(ep->emm[0])
522 case 0x82:
523 ep->type = UNIQUE;
524 memset(ep->hexserial, 0, 8);
525 memcpy(ep->hexserial, ep->emm + 3, 6);
527 rdr_log_dbg_sensitive(rdr, D_EMM, "UNIQUE, ep->hexserial = {%s}",
528 cs_hexdump(1, ep->hexserial, 6, tmp_dbg, sizeof(tmp_dbg)));
530 rdr_log_dbg_sensitive(rdr, D_EMM, "UNIQUE, rdr->hexserial = {%s}",
531 cs_hexdump(1, rdr->hexserial, 6, tmp_dbg, sizeof(tmp_dbg)));
533 return (!memcmp(rdr->hexserial, ep->hexserial, 6));
534 break;
536 case 0x84:
537 ep->type = SHARED;
538 memset(ep->hexserial, 0, 8);
539 memcpy(ep->hexserial, ep->emm + 5, 3); // don't include custom byte; this way the network also knows SA
540 i = get_prov_index(rdr, ep->emm + 3);
542 rdr_log_dbg_sensitive(rdr, D_EMM, "SHARED, ep->hexserial = {%s}",
543 cs_hexdump(1, ep->hexserial, 3, tmp_dbg, sizeof(tmp_dbg)));
545 if(i == -1) // provider not found on this card
546 { return 0; } //do not pass this EMM
548 rdr_log_dbg_sensitive(rdr, D_EMM, "SHARED, rdr->sa[%i] = {%s}", i,
549 cs_hexdump(1, rdr->sa[i], 3, tmp_dbg, sizeof(tmp_dbg)));
551 return (!memcmp(rdr->sa[i], ep->hexserial, 3));
552 break;
554 // Unknown EMM types, but allready subbmited to dev's
555 // FIXME: Drop EMM's until there are implemented
556 case 0x83:
557 ep->type = GLOBAL;
558 rdr_log_dbg(rdr, D_EMM, "GLOBAL, PROVID: %04X", (ep->emm[3] << 8) | ep->emm[4]);
559 return 1;
560 /* EMM-G manadge ppv by provid
561 83 00 74 33 41 04 70 00 BF 20 A1 15 48 1B 88 FF
562 CF F5 50 CB 6F E1 26 A2 70 02 8F D0 07 6A 13 F9
563 50 F9 61 88 FB E4 B8 03 EF 68 C9 54 EB C0 51 2E
564 9D F9 E1 4A D9 A6 3F 5D 7A 1E B0 6E 3D 9B 93 E7
565 5A E8 D4 AE 29 B9 37 07 5A 43 C8 F2 DE BD F8 BA
566 69 DC A4 87 C2 FA 25 87 87 42 47 67 AE B7 1A 54
567 CA F6 B7 EC 15 0A 67 1C 59 F8 B9 B8 6F 7D 58 94
568 24 63 17 15 58 1E 59
571 case 0x88:
572 case 0x89:
573 // EMM-G ?
574 ep->type = UNKNOWN;
575 return 0;
577 default:
578 ep->type = UNKNOWN;
579 return 1;
583 static int32_t seca_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count)
585 if(*emm_filters == NULL)
587 const unsigned int max_filter_count = 1 + (2 * rdr->nprov);
588 if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter)))
590 return ERROR;
593 struct s_csystem_emm_filter *filters = *emm_filters;
594 *filter_count = 0;
596 int32_t idx = 0;
598 filters[idx].type = EMM_UNIQUE;
599 filters[idx].enabled = 1;
600 filters[idx].filter[0] = 0x82;
601 filters[idx].mask[0] = 0xFF;
602 memcpy(&filters[idx].filter[1], rdr->hexserial, 6);
603 memset(&filters[idx].mask[1], 0xFF, 6);
604 idx++;
606 int32_t prov;
607 for(prov = 0; prov < rdr->nprov; prov++)
609 // if sa == null skip update by shared & global (provid inactive)
610 if(!memcmp(rdr->sa[prov], "\x00\x00\x00", 3))
612 continue;
615 filters[idx].type = EMM_GLOBAL; // global by provider
616 filters[idx].enabled = 1;
617 filters[idx].filter[0] = 0x83;
618 filters[idx].mask[0] = 0xFF;
619 memcpy(&filters[idx].filter[1], &rdr->prid[prov][2], 2);
620 memset(&filters[idx].mask[1], 0xFF, 2);
621 idx++;
623 filters[idx].type = EMM_SHARED;
624 filters[idx].enabled = 1;
625 filters[idx].filter[0] = 0x84;
626 filters[idx].mask[0] = 0xFF;
627 memcpy(&filters[idx].filter[1], &rdr->prid[prov][2], 2);
628 memset(&filters[idx].mask[1], 0xFF, 2);
629 memcpy(&filters[idx].filter[3], &rdr->sa[prov], 3);
630 memset(&filters[idx].mask[3], 0xFF, 3);
631 idx++;
634 *filter_count = idx;
637 return OK;
640 static int32_t seca_do_emm(struct s_reader *reader, EMM_PACKET *ep)
642 def_resp;
643 uint8_t ins40[] = { 0xc1, 0x40, 0x00, 0x00, 0x00 };
644 int32_t i, ins40data_offset;
645 int32_t emm_length = ((ep->emm[1] & 0x0f) << 8) + ep->emm[2];
646 uint8_t *prov_id_ptr;
648 switch(ep->type)
650 case SHARED:
651 ins40[3] = ep->emm[9];
652 ins40[4] = emm_length - 0x07;
653 ins40data_offset = 10;
654 prov_id_ptr = ep->emm + 3;
655 break;
657 case UNIQUE:
658 ins40[3] = ep->emm[12];
659 ins40[4] = emm_length - 0x0A;
660 ins40data_offset = 13;
661 prov_id_ptr = ep->emm + 9;
662 break;
664 case GLOBAL:
665 ins40[3] = ep->emm[6];
666 ins40[4] = emm_length - 0x04;
667 ins40data_offset = 7;
668 prov_id_ptr = ep->emm + 3;
669 break;
671 default:
672 rdr_log(reader, "EMM: Congratulations, you have discovered a new EMM on SECA.");
673 rdr_log(reader, "This has not been decoded yet, so send this output to authors:");
674 rdr_log_dump(reader, ep->emm, emm_length + 3, "EMM:");
675 return ERROR;
678 i = get_prov_index(reader, prov_id_ptr);
679 if(i == -1)
681 rdr_log(reader, "EMM: skipped since provider id doesnt match");
682 return SKIPPED;
685 ins40[2] = (ep->emm[ins40data_offset - 2] & 0xF0) | (i & 0x0F);
686 write_cmd(ins40, ep->emm + ins40data_offset); // emm request
688 if(cta_res[0] == 0x97)
690 if(!(cta_res[1] & 4)) // date updated
692 set_provider_info(reader, i);
694 else
696 rdr_log(reader, "EMM: Update not necessary.");
698 return OK; // Update not necessary
701 if((cta_res[0] == 0x90) && ((cta_res[1] == 0x00) || (cta_res[1] == 0x19)))
703 if(ep->type == GLOBAL) // do not print new provider info after global emm
705 return OK;
708 if(set_provider_info(reader, i) == OK) // after successful EMM, print32_t new provider info
710 return OK;
713 return ERROR;
716 static int32_t seca_card_info(struct s_reader *reader)
718 def_resp;
719 static const uint8_t ins16[] = { 0xc1, 0x16, 0x00, 0x00, 0x06 }; // get nr. of providers
720 int32_t prov = 0;
721 uint16_t pmap = 0; // provider-maptable
723 int16_t tries = 0;
724 int16_t i = 0;
726 while(reader->nprov == 0 && tries < 254)
728 write_cmd(ins16, NULL); // read nr of providers
729 pmap = cta_res[2] << 8 | cta_res[3];
731 for(reader->nprov = 0, i = pmap; i; i >>= 1)
733 reader->nprov += i & 1;
736 if(reader->nprov == 0)
738 tries++;
739 continue;
743 for(prov = 0; prov < reader->nprov; prov++)
745 set_provider_info(reader, prov);
747 return OK;
750 const struct s_cardsystem reader_seca =
752 .desc = "seca",
753 .caids = (uint16_t[]){ 0x01, 0 },
754 .do_emm = seca_do_emm,
755 .do_ecm = seca_do_ecm,
756 .card_info = seca_card_info,
757 .card_init = seca_card_init,
758 .get_emm_type = seca_get_emm_type,
759 .get_emm_filter = seca_get_emm_filter,
762 #endif