/* * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "../globals.h" #ifdef CARDREADER_SC8IN1 #include "../oscam-lock.h" #include "../oscam-string.h" #include "../oscam-time.h" #include "atr.h" #include "ifd_phoenix.h" #include "io_serial.h" #define OK 0 #define ERROR 1 struct s_sc8in1_display { char *text; uint16_t text_length; uint16_t char_change_time; uint16_t last_char; uint8_t blocking; struct s_sc8in1_display *next; }; struct sc8in1_data { struct termios stored_termio[8]; uint16_t current_slot; uint32_t current_baudrate; struct s_reader *current_reader; unsigned char cardstatus; unsigned char mcr_type; CS_MUTEX_LOCK sc8in1_lock; struct s_sc8in1_display *display; CS_MUTEX_LOCK sc8in1_display_lock; unsigned char display_running; pthread_t display_thread; }; #ifdef WITH_DEBUG static int32_t Sc8in1_DebugSignals(struct s_reader *reader, uint16_t slot, const char *extra) { uint32_t msr; if(ioctl(reader->handle, TIOCMGET, &msr) < 0) { return ERROR; } rdr_log_dbg(reader, D_DEVICE, "SC8in1: Signals(%s): Slot: %i, DTR: %u, RTS: %u", extra, slot, msr & TIOCM_DTR ? 1 : 0, msr & TIOCM_RTS ? 1 : 0); return OK; } #else #define Sc8in1_DebugSignals(a, b, c) {} #endif static int32_t Sc8in1_NeedBaudrateChange(struct s_reader *reader, uint32_t desiredBaudrate, struct termios *current, struct termios *new, uint8_t cmdMode) { struct sc8in1_data *crdr_data = reader->crdr_data; // Returns 1 if we need to change the baudrate if((desiredBaudrate != crdr_data->current_baudrate) || (reader->mhz != reader->cardmhz) || (cmdMode == 0 && memcmp(current, new, sizeof(struct termios)))) { rdr_log_dbg(reader, D_TRACE, "Sc8in1_NeedBaudrateChange 1"); return 1; } rdr_log_dbg(reader, D_TRACE, "Sc8in1_NeedBaudrateChange 0"); return 0; } static int32_t Sc8in1_SetBaudrate(struct s_reader *reader, uint32_t baudrate, struct termios *termio, uint8_t cmdMode) { struct sc8in1_data *crdr_data = reader->crdr_data; /* Get current settings */ struct termios tio; if(termio == NULL) { call(tcgetattr(reader->handle, &tio) != 0); } else { tio = *termio; if(baudrate == 0) { baudrate = reader->current_baudrate; if(baudrate == 0) { baudrate = 9600; } } } rdr_log_dbg(reader, D_IFD, "Sc8in1 Setting baudrate to %u", baudrate); rdr_log_dbg(reader, D_TRACE, "Sc8in1 Setting baudrate to %u, reader br=%u, currentBaudrate=%u, cmdMode=%u", baudrate, reader->current_baudrate, crdr_data->current_baudrate, cmdMode); call(IO_Serial_SetBitrate(reader, baudrate, &tio)); crdr_data->current_baudrate = baudrate; call(IO_Serial_SetProperties(reader, tio)); if(cmdMode == 0) { reader->current_baudrate = baudrate; } return OK; } static int32_t sc8in1_tcdrain(struct s_reader *reader) { while(1) { int32_t tcdrain_ret = tcdrain(reader->handle); if(tcdrain_ret == -1) { if(errno == EINTR) { //try again in case of Interrupted system call continue; } else { rdr_log(reader, "ERROR: %s: (errno=%d %s)", __func__, errno, strerror(errno)); } return ERROR; } break; } return OK; } static int32_t sc8in1_command(struct s_reader *reader, unsigned char *buff, uint16_t lenwrite, uint16_t lenread, uint8_t enableEepromWrite, unsigned char UNUSED(getStatusMode), uint8_t selectSlotMode) { struct sc8in1_data *crdr_data = reader->crdr_data; struct termios termio, termiobackup; uint32_t currentBaudrate = 0; if(! reader->handle) { rdr_log(reader, "ERROR: SC8in1 Command no valid handle"); return ERROR; } Sc8in1_DebugSignals(reader, reader->slot, "CMD10"); // switch SC8in1 to command mode IO_Serial_DTR_Set(reader); tcflush(reader->handle, TCIOFLUSH); // backup data tcgetattr(reader->handle, &termio); memcpy(&termiobackup, &termio, sizeof(termio)); if(selectSlotMode) { if(crdr_data->current_slot != 0) { memcpy(&crdr_data->stored_termio[crdr_data->current_slot - 1], &termiobackup, sizeof(termiobackup)); //not if current_slot is undefined } } // set communication parameters termio.c_oflag = 0; termio.c_lflag = 0; termio.c_cc[VTIME] = 1; // working termio.c_cflag = B9600 | CS8 | CREAD | CLOCAL; // Do we need to set the baudrate? if(Sc8in1_NeedBaudrateChange(reader, 9600, &termiobackup, &termio, 1)) { rdr_log_dbg(reader, D_TRACE, "Sc8in1_NeedBaudrateChange for SC8in1 command"); // save current baudrate for later restore currentBaudrate = crdr_data->current_baudrate; crdr_data->current_baudrate = 9600; cfsetospeed(&termio, B9600); cfsetispeed(&termio, B9600); rdr_log_dbg(reader, D_DEVICE, "standard baudrate: cardmhz=%d mhz=%d -> effective baudrate %u", reader->cardmhz, reader->mhz, 9600); } if(tcsetattr(reader->handle, TCSANOW, &termio) < 0) { rdr_log(reader, "ERROR: SC8in1 Command error in set RS232 attributes"); return ERROR; } if(reader->sc8in1_dtrrts_patch == 1) { IO_Serial_DTR_Set(reader); } Sc8in1_DebugSignals(reader, reader->slot, "CMD11"); // enable EEPROM write if(enableEepromWrite) { unsigned char eepromBuff[3]; eepromBuff[0] = 0x70; eepromBuff[1] = 0xab; eepromBuff[2] = 0xba; rdr_log_dump_dbg(reader, D_DEVICE, eepromBuff, 3, "Sending:"); if(!write(reader->handle, eepromBuff, 3)) { rdr_log(reader, "SC8in1 Command write EEPROM error"); return ERROR; } tcflush(reader->handle, TCIOFLUSH); } // write cmd rdr_log_dump_dbg(reader, D_DEVICE, buff, lenwrite, "Sending:"); int32_t dataWritten = 0, dataToWrite = lenwrite; while(dataWritten < lenwrite) { int32_t written = write(reader->handle, buff, dataToWrite); if(written == -1) { rdr_log(reader, "SC8in1 Command write error"); return ERROR; } if(written == lenwrite) { break; } else { dataWritten += written; dataToWrite -= written; } } sc8in1_tcdrain(reader); if(IO_Serial_Read(reader, 0, 1000000, lenread, buff) == ERROR) { rdr_log(reader, "SC8in1 Command read error"); return ERROR; } // Workaround for systems where tcdrain doesnt work properly if(lenread <= 0 && crdr_data->mcr_type) { unsigned char buff_echo_hack[2] = { 0x65, 'A' }; rdr_log_dump_dbg(reader, D_DEVICE, &buff_echo_hack[0], 2, "Sending:"); if(write(reader->handle, &buff_echo_hack[0], 2) != 2) { rdr_log(reader, "SC8in1 Echo command write error"); return ERROR; } sc8in1_tcdrain(reader); if(IO_Serial_Read(reader, 0, 1000000, 1, &buff_echo_hack[0]) == ERROR) { rdr_log(reader, "SC8in1 Echo command read error"); return ERROR; } if(buff_echo_hack[0] != 'A') { rdr_log(reader, "SC8in1 Echo command read wrong character"); } } if(selectSlotMode) { memcpy(&termiobackup, &crdr_data->stored_termio[selectSlotMode - 1], sizeof(termiobackup)); if(Sc8in1_NeedBaudrateChange(reader, reader->current_baudrate, &termio, &termiobackup, 1)) { rdr_log_dbg(reader, D_TRACE, "Sc8in1_SetTermioForSlot for select slot"); if(Sc8in1_SetBaudrate(reader, reader->current_baudrate, &termiobackup, 0)) { rdr_log(reader, "ERROR: SC8in1 Command Sc8in1_SetBaudrate"); return ERROR; } } else { if(tcsetattr(reader->handle, TCSANOW, &termiobackup) < 0) { rdr_log(reader, "ERROR: SC8in1 Command error in set RS232 attributes"); return ERROR; } } } else { // restore baudrate only if changed if(currentBaudrate) { if(Sc8in1_SetBaudrate(reader, currentBaudrate, &termiobackup, 1)) { rdr_log(reader, "ERROR: SC8in1 selectslot restore Bitrate attributes"); return ERROR; } } else { // restore data if(tcsetattr(reader->handle, TCSANOW, &termiobackup) < 0) { rdr_log(reader, "ERROR: SC8in1 Command error in restore RS232 attributes"); return ERROR; } } } Sc8in1_DebugSignals(reader, reader->slot, "CMD12"); if(reader->sc8in1_dtrrts_patch == 1) { IO_Serial_DTR_Set(reader); } tcflush(reader->handle, TCIOFLUSH); // switch SC8in1 to normal mode IO_Serial_DTR_Clr(reader); Sc8in1_DebugSignals(reader, reader->slot, "CMD13"); return OK; } static int32_t mcrReadStatus(struct s_reader *reader, unsigned char *status) { unsigned char buff[2] = ""; buff[0] = 0x3f; if(sc8in1_command(reader, buff, 1, 2, 0, 1, 0) < 0) { return ERROR; } status[0] = buff[0]; status[1] = buff[1]; return OK; } static int32_t sc8in1ReadStatus(struct s_reader *reader, unsigned char *status) { unsigned char buff[9]; // read 1 echo byte + 8 status bytes buff[0] = 0x47; if(sc8in1_command(reader, buff, 1, 9, 0, 1, 0) < 0) { return ERROR; } memcpy(&status[0], &buff[1], 8); return OK; } static int32_t mcrReadType(struct s_reader *reader, unsigned char *type) { unsigned char buff[1]; buff[0] = 0x74; if(sc8in1_command(reader, buff, 1, 1, 0, 0, 0) < 0) { return ERROR; } type[0] = buff[0]; return OK; } static int32_t mcrReadVersion(struct s_reader *reader, unsigned char *version) { unsigned char buff[1]; buff[0] = 0x76; if(sc8in1_command(reader, buff, 1, 1, 0, 0, 0) < 0) { return ERROR; } version[0] = buff[0]; return OK; } static int32_t mcrReadSerial(struct s_reader *reader, unsigned char *serial) { unsigned char buff[2]; buff[0] = 0x6e; if(sc8in1_command(reader, buff, 1, 2, 0, 0, 0) < 0) { return ERROR; } serial[0] = buff[1]; serial[1] = buff[0]; return OK; } /*static int32_t mcrWriteDisplayRaw(struct s_reader *reader, unsigned char data[7]) { unsigned char buff[8]; buff[0] = 0x64; memcpy(&buff[1], &data[0], 7); if (sc8in1_command(reader, buff, 8, 0, 0, 0, 0) < 0) return ERROR; return OK; }*/ static int32_t mcrWriteDisplayAscii(struct s_reader *reader, unsigned char data, unsigned char timeout) { unsigned char buff[3]; buff[0] = 0x61; buff[1] = data; buff[2] = timeout; if(sc8in1_command(reader, buff, 3, 0, 0, 0, 0) < 0) { return ERROR; } return OK; } static int32_t mcrWriteClock(struct s_reader *reader, unsigned char saveClock, unsigned char clock_val[2]) { unsigned char buff[3]; buff[0] = 0x63; buff[1] = clock_val[0]; buff[2] = clock_val[1]; if(sc8in1_command(reader, buff, 3, 0, 0, 0, 0) < 0) { return ERROR; } if(saveClock) { buff[0] = 0x6d; if(sc8in1_command(reader, buff, 1, 0, 1, 0, 0) < 0) { return ERROR; } } return OK; } static int32_t mcrReadClock(struct s_reader *reader, unsigned char *clock_val) { unsigned char buff[2]; buff[0] = 0x67; if(sc8in1_command(reader, buff, 1, 2, 0, 0, 0) < 0) { return ERROR; } clock_val[0] = buff[0]; clock_val[1] = buff[1]; return OK; } static int32_t mcrWriteTimeout(struct s_reader *reader, unsigned char timeout[2]) { unsigned char buff[3]; buff[0] = 0x6f; buff[1] = timeout[0]; buff[2] = timeout[1]; if(sc8in1_command(reader, buff, 3, 0, 1, 0, 0) < 0) { return ERROR; } return OK; } static int32_t mcrReadTimeout(struct s_reader *reader, unsigned char *timeout) { unsigned char buff[2]; buff[0] = 0x72; if(sc8in1_command(reader, buff, 1, 2, 0, 0, 0) < 0) { return ERROR; } timeout[0] = buff[1]; timeout[1] = buff[0]; return OK; } static int32_t mcrSelectSlot(struct s_reader *reader, unsigned char slot) { // Select slot for MCR device. // Parameter slot is from 1-8 unsigned char buff[2]; buff[0] = 0x73; buff[1] = slot - 1; if(sc8in1_command(reader, buff, 2, 0, 0, 0, slot) < 0) { return ERROR; } return OK; } static int32_t sc8in1SelectSlot(struct s_reader *reader, unsigned char slot) { // Select slot for SC8in1 device. // Parameter slot is from 1-8 unsigned char buff[6]; buff[0] = 0x53; buff[1] = slot & 0x0F; // Read 6 Bytes: 2 Bytes write cmd and 4 unknown Bytes. if(sc8in1_command(reader, buff, 2, 6, 0, 0, slot) < 0) { return ERROR; } return OK; } static int32_t MCR_DisplayText(struct s_reader *reader, char *text, uint16_t text_len, uint16_t ch_time, uint8_t blocking) { struct sc8in1_data *crdr_data = reader->crdr_data; struct s_sc8in1_display *display; if(cs_malloc(&display, sizeof(struct s_sc8in1_display))) { if(!cs_malloc(&display->text, text_len)) { rdr_log(reader, "MCR_DisplayText: Out of memory."); NULLFREE(display); return ERROR; } if (display) { memcpy(display->text, text, text_len); display->text_length = text_len; display->char_change_time = ch_time; display->last_char = 0; display->blocking = blocking; display->next = NULL; } cs_writelock(__func__, &crdr_data->sc8in1_display_lock); if(crdr_data->display == NULL) { crdr_data->display = display; } else { struct s_sc8in1_display *d = crdr_data->display; while(d != NULL) { if(d->next == NULL) { d->next = display; break; } else { d = d->next; } } } cs_writeunlock(__func__, &crdr_data->sc8in1_display_lock); } else { rdr_log(reader, "MCR_DisplayText: Out of memory."); return ERROR; } return OK; } static int32_t mcrHelloOscam(struct s_reader *reader) { // Display "OSCam" on MCR display char helloOscam[5] = {'O', 'S', 'C', 'a', 'm'}; return MCR_DisplayText(reader, &helloOscam[0], 5, 100, 1); } static int32_t mcr_generateStatisticsForDisplay(struct s_reader *reader) { // show number of clients struct s_client *cl; uint16_t numClients = 0; for(cl = first_client; cl ; cl = cl->next) { if(cl->typ == 'c') { numClients++; } } char msg[8] = { 0 }; int msgLen = snprintf(&msg[0], 8, "CN%i", numClients); if(msgLen > 0 && MCR_DisplayText(reader, msg, msgLen, 300, 0)) { return ERROR; } return OK; } static void *mcr_update_display_thread(void *param) { const uint16_t DEFAULT_SLEEP_TIME = 100; const int32_t STATISTICS_UPDATE_SECONDS = 60; struct s_reader *reader = (struct s_reader *)param; struct sc8in1_data *crdr_data = reader->crdr_data; time_t lastStatisticUpdateTime = time((time_t *)0); if(reader->typ != R_SC8in1 || ! crdr_data->mcr_type) { rdr_log(reader, "Error: mcr_update_display_thread reader no MCR8in1 reader"); pthread_exit(NULL); } set_thread_name(__func__); while(crdr_data->display_running) { uint16_t display_sleep = DEFAULT_SLEEP_TIME; // Update statistics time_t currentTime = time((time_t *)0); if(currentTime - lastStatisticUpdateTime >= STATISTICS_UPDATE_SECONDS) { if(mcr_generateStatisticsForDisplay(reader)) { rdr_log(reader, "ERROR: mcr_generateStatisticsForDisplay"); } lastStatisticUpdateTime = currentTime; } cs_writelock(__func__, &crdr_data->sc8in1_display_lock); if(crdr_data->display != NULL) // is there something to display? { cs_writeunlock(__func__, &crdr_data->sc8in1_display_lock); display_sleep = crdr_data->display->char_change_time; // display the next character cs_writelock(__func__, &crdr_data->sc8in1_lock); if(crdr_data->display->blocking) { uint16_t i = 0; for(i = 0; i < crdr_data->display->text_length; i++) { if(mcrWriteDisplayAscii(crdr_data->current_reader, crdr_data->display->text[++crdr_data->display->last_char - 1], 0xFF)) { rdr_log(reader, "SC8in1: Error in mcr_update_display_thread write"); } cs_sleepms(display_sleep); } } else { if(mcrWriteDisplayAscii(crdr_data->current_reader, crdr_data->display->text[++crdr_data->display->last_char - 1], 0xFF)) { rdr_log(reader, "SC8in1: Error in mcr_update_display_thread write"); } } cs_writeunlock(__func__, &crdr_data->sc8in1_lock); // remove the display struct if the text has been shown completely if(crdr_data->display->last_char == crdr_data->display->text_length) { cs_writelock(__func__, &crdr_data->sc8in1_display_lock); struct s_sc8in1_display *next = crdr_data->display->next; NULLFREE(crdr_data->display->text); NULLFREE(crdr_data->display); crdr_data->display = next; cs_writeunlock(__func__, &crdr_data->sc8in1_display_lock); } } else { cs_writeunlock(__func__, &crdr_data->sc8in1_display_lock); } cs_sleepms(display_sleep); } pthread_exit(NULL); return NULL; } static int32_t readSc8in1Status(struct s_reader *reader) { struct sc8in1_data *crdr_data = reader->crdr_data; // Reads the card status // // the bits in the return bytes: // bit0=1 means Slot1=Smartcard inside // bit1=1 means Slot2=Smartcard inside // bit2=1 means Slot3=Smartcard inside // bit3=1 means Slot4=Smartcard inside // bit4=1 means Slot5=Smartcard inside // bit5=1 means Slot6=Smartcard inside // bit6=1 means Slot7=Smartcard inside // bit7=1 means Slot8=Smartcard inside tcflush(reader->handle, TCIOFLUSH); if(crdr_data->mcr_type) { unsigned char buff[2]; if(mcrReadStatus(reader, &buff[0])) { return (-1); } tcflush(reader->handle, TCIOFLUSH); return buff[0]; } else { unsigned char buff[8]; if(sc8in1ReadStatus(reader, &buff[0])) { return (-1); } if(buff[0] != 0x90) { return (-1); } tcflush(reader->handle, TCIOFLUSH); return buff[1]; } } static int32_t Sc8in1_Selectslot(struct s_reader *reader, uint16_t slot) { // selects the Smartcard Socket "slot" // struct sc8in1_data *crdr_data = reader->crdr_data; if(slot == crdr_data->current_slot) { return OK; } rdr_log_dbg(reader, D_TRACE, "SC8in1: select slot %i", slot); #ifdef WITH_DEBUG struct timeb tv_start, tv_end; cs_ftime(&tv_start); #endif int32_t status = ERROR; if(crdr_data->mcr_type) { status = mcrSelectSlot(reader, slot); } else { status = sc8in1SelectSlot(reader, slot); } if(status == OK) { crdr_data->current_reader = reader; crdr_data->current_slot = slot; } #ifdef WITH_DEBUG cs_ftime(&tv_end); rdr_log_dbg(reader, D_DEVICE, "SC8in1 Selectslot in %"PRId64" ms", comp_timeb(&tv_end, &tv_start)); #endif return status; } static int32_t Sc8in1_Card_Changed(struct s_reader *reader) { // returns the SC8in1 Status // 0= no card was changed (inserted or removed) // -1= one ore more cards were changed (inserted or removed) int32_t result; int32_t lineData; if(reader->handle == 0) { return 0; } ioctl(reader->handle, TIOCMGET, &lineData); result = (lineData & TIOCM_CTS) / TIOCM_CTS; return result - 1; } static int32_t Sc8in1_GetStatus(struct s_reader *reader, int32_t *in) { // Only same thread my access serial port struct sc8in1_data *crdr_data = reader->crdr_data; if((crdr_data->current_slot == reader->slot && Sc8in1_Card_Changed(reader)) || *in == -1) { int32_t i = readSc8in1Status(reader); //read cardstatus if(i < 0) { rdr_log(reader, "Sc8in1_GetStatus Error"); return ERROR; } crdr_data->cardstatus = i; rdr_log_dbg(reader, D_TRACE, "SC8in1: Card status changed; cardstatus=0x%X", crdr_data->cardstatus); } *in = (crdr_data->cardstatus & 1 << (reader->slot - 1)); return OK; } static int32_t Sc8in1_Init(struct s_reader *reader) { struct sc8in1_data *crdr_data = reader->crdr_data; //additional init, Phoenix_Init is also called for Sc8in1 ! struct termios termio; int32_t i, speed, retval; uint16_t sc8in1_clock = 0; //unsigned char buff[3]; Sc8in1_DebugSignals(reader, reader->slot, "I-1"); // Clr DTR, which is set by phoenix_init IO_Serial_DTR_Clr(reader); tcgetattr(reader->handle, &termio); for(i = 0; i < 8; i++) { //init all stored termios to default comm settings after device init, before ATR memcpy(&crdr_data->stored_termio[i], &termio, sizeof(termio)); } // Init sc8in1 config crdr_data->mcr_type = 0; crdr_data->current_reader = reader; // check for a MCR device and how many slots it has. unsigned char mcrType[1]; mcrType[0] = 0; // at least fritzbox7170 needs to issue this twice mcrReadType(reader, &mcrType[0]); mcrType[0] = 0; if(! mcrReadType(reader, &mcrType[0])) { if(mcrType[0] == 4 || mcrType[0] == 8) { crdr_data->mcr_type = mcrType[0]; rdr_log(reader, "SC8in1: MCR%u detected for device %s", crdr_data->mcr_type, reader->device); unsigned char version[1]; version[0] = 0; if(! mcrReadVersion(reader, &version[0])) { rdr_log(reader, "SC8in1: Version %u for device %s", (unsigned char)version[0], reader->device); } unsigned char serial[2]; serial[0] = 0; serial[1] = 0; if(! mcrReadSerial(reader, &serial[0])) { rdr_log(reader, "SC8in1: Serial %u for device %s", (uint16_t)serial[0], reader->device); } //now work-around the problem that timeout of MCR has to be 0 in case of USB unsigned char timeout[2]; timeout[0] = 0; timeout[1] = 0; retval = mcrReadTimeout(reader, &timeout[0]); if(retval) { rdr_log(reader, "SC8in1: Error reading timeout."); } else { rdr_log(reader, "SC8in1: Timeout %u for device %s", (uint16_t)timeout[0], reader->device); } if((strstr(reader->device, "USB")) && (retval == ERROR || timeout[0] != 0 || timeout[1] != 0)) //assuming we are connected thru USB and timeout is undetected or not zero { rdr_log(reader, "SC8in1: Detected Sc8in1 device connected with USB, setting timeout to 0 and writing to EEPROM"); timeout[0] = 0; timeout[1] = 0; if(mcrWriteTimeout(reader, timeout)) { rdr_log(reader, "SC8in1: Error writing timeout."); } } // Start display thread crdr_data->display_running = 1; start_thread("mcr_update_display", mcr_update_display_thread, (void *)(reader), &crdr_data->display_thread, 0, 1); } } if(! crdr_data->mcr_type) { tcflush(reader->handle, TCIOFLUSH); // a non MCR reader might give longer answer } Sc8in1_DebugSignals(reader, reader->slot, "I0"); struct s_reader *rdr; LL_ITER itr = ll_iter_create(configured_readers); while((rdr = ll_iter_next(&itr))) //also do this for disabled readers, so we configure the clocks right for all readers if(rdr->crdr_data == crdr_data) //corresponding slot { //check slot boundaries int32_t upper_slot = (crdr_data->mcr_type) ? crdr_data->mcr_type : 8; //set upper limit to 8 for non MCR readers if(rdr->slot <= 0 || rdr->slot > upper_slot) { rdr_log(reader, "ERROR: device %s has invalid slot number %i", rdr->device, rdr->slot); return ERROR; } // set initial current_baudrate which is needed by sc8in1_command rdr->current_baudrate = reader->current_baudrate; if(crdr_data->mcr_type) { //set RTS for every slot to 1 to prevent jitter/glitch detection problems Sc8in1_DebugSignals(reader, rdr->slot, "I1"); mcrSelectSlot(reader, rdr->slot); Sc8in1_DebugSignals(reader, rdr->slot, "I2"); IO_Serial_RTS_Set(reader); Sc8in1_DebugSignals(reader, rdr->slot, "I3"); //calculate clock-bits switch(rdr->mhz) { case 357: case 358: speed = 0; break; case 368: case 369: speed = 1; break; case 600: speed = 2; break; case 800: speed = 3; break; default: speed = 0; rdr_log(reader, "ERROR: Sc8in1 cannot set clockspeed to %d", rdr->mhz); break; } sc8in1_clock |= (speed << ((rdr->slot - 1) * 2)); } } if(crdr_data->mcr_type) { sc8in1_clock = ((sc8in1_clock & 0xFF) << 8) | ((sc8in1_clock & 0xFF00) >> 8); //set clockspeeds for all slots unsigned char clockspeed[2]; memcpy(&clockspeed, &sc8in1_clock, 2); if(mcrWriteClock(reader, 0, clockspeed)) { rdr_log(reader, "ERROR: Sc8in1 cannot set clockspeed to %d", (uint16_t)clockspeed[0]); } // Clear RTS again itr = ll_iter_create(configured_readers); while((rdr = ll_iter_next(&itr))) { if(rdr->crdr_data == crdr_data) { Sc8in1_DebugSignals(reader, rdr->slot, "I4"); mcrSelectSlot(reader, rdr->slot); Sc8in1_DebugSignals(reader, rdr->slot, "I5"); IO_Serial_RTS_Clr(reader); Sc8in1_DebugSignals(reader, rdr->slot, "I6"); // Discard ATR unsigned char buff[1]; while(! IO_Serial_Read(reader, 0, 500000, 1, &buff[0])) { cs_sleepms(1); } tcflush(reader->handle, TCIOFLUSH); } } //DEBUG get clockspeeds if(mcrReadClock(reader, &clockspeed[0])) { rdr_log(reader, "ERROR: Sc8in1 cannot read clockspeed"); } static char *clock_mhz[] = { "3,57", "3,68", "6,00", "8,00" }; uint16_t result = clockspeed[0] << 8 | clockspeed[1]; for(i = 0; i < 8; i++) { rdr_log(reader, "Slot %i is clocked with %s mhz", i + 1, clock_mhz[(result >> (i * 2)) & 0X0003]); } } Sc8in1_Selectslot(reader, reader->slot); i = -1; //Flag for GetStatus init Sc8in1_GetStatus(reader, &i); //Initialize cardstatus // Gimmick if(crdr_data->mcr_type) { mcrHelloOscam(reader); } return OK; } static int32_t Sc8in1_GetActiveHandle(struct s_reader *reader, uint8_t onlyEnabledReaders) { // Returns a handle to the serial port, if it exists in some other // slot of the same physical reader. // Or returns 0 otherwise. struct sc8in1_data *crdr_data = reader->crdr_data; struct s_reader *rdr; LL_ITER itr = ll_iter_create(configured_readers); while((rdr = ll_iter_next(&itr))) { if(rdr->typ == R_SC8in1) { if((reader != rdr) && (crdr_data == rdr->crdr_data) && rdr->handle != 0 && (onlyEnabledReaders ? rdr->enable != 0 : 1)) { return rdr->handle; } } } return OK; } static int32_t Sc8in1_Close(struct s_reader *reader) { // Check if we are the last active slot for the reader, // then close the serial port. Otherwise select next acive slot. struct sc8in1_data *crdr_data = reader->crdr_data; rdr_log_dbg(reader, D_IFD, "Closing SC8in1 device %s", reader->device); bool status = ERROR; if(Sc8in1_GetActiveHandle(reader, 1)) { rdr_log_dbg(reader, D_IFD, "Just deactivating SC8in1 device %s", reader->device); reader->written = 0; status = OK; // select next active reader slot, so getstatus still works if(crdr_data->current_slot == reader->slot) { struct s_reader *rdr; for(rdr = first_active_reader; rdr; rdr = rdr->next) { if(rdr->typ == R_SC8in1) { if((reader != rdr) && (crdr_data == rdr->crdr_data) && rdr->handle != 0) { status = Sc8in1_Selectslot(rdr, rdr->slot); break; } } } } } else { if(crdr_data->mcr_type) { // disable reader threads crdr_data->display_running = 0; SAFE_THREAD_JOIN(crdr_data->display_thread, NULL); } // disable other slots struct s_reader *rdr; LL_ITER itr = ll_iter_create(configured_readers); while((rdr = ll_iter_next(&itr))) { if(rdr->typ == R_SC8in1) { if((reader != rdr) && (crdr_data == rdr->crdr_data)) { rdr->handle = 0; } } } // close serial port if(reader->handle != 0) { status = IO_Serial_Close(reader); reader->handle = 0; } } return status; } static int32_t Sc8in1_SetSlotForReader(struct s_reader *reader) { // Sets the slot for the reader if it is not set already int32_t pos = cs_strlen(reader->device) - 2; //this is where : should be located; is also valid length of physical device name if(reader->device[pos] != 0x3a) //0x3a = ":" { rdr_log(reader, "ERROR: '%c' detected instead of slot separator `:` at second to last position of device %s", reader->device[pos], reader->device); } reader->slot = (uint16_t)reader->device[pos + 1] - 0x30; return OK; } static int32_t Sc8in1_InitLocks(struct s_reader *reader) { // Create SC8in1_Configs and init locks. // Method is called once for every reader. // If there is already a Sc8in1 reader configured with the // same device (means same reader, different slot) then use // its sc8in1_config, otherwise create a new sc8in1_config and return. Sc8in1_SetSlotForReader(reader); // Get device name int32_t pos = cs_strlen(reader->device) - 2; if(pos <= 0) { return ERROR; } if(reader->device[pos] != 0x3a) //0x3a = ":" { rdr_log(reader, "ERROR: Sc8in1_InitLocks: '%c' detected instead of slot separator `:` at second to last position of device %s", reader->device[pos], reader->device); } unsigned char savePos = reader->device[pos]; reader->device[pos] = 0; uint8_t reader_config_exists = 0; struct s_reader *rdr; LL_ITER itr = ll_iter_create(configured_readers); while((rdr = ll_iter_next(&itr))) { if(rdr->typ == R_SC8in1 && rdr != reader) { unsigned char save = rdr->device[pos]; rdr->device[pos] = 0; //set to 0 so we can compare device names if(!strcmp(reader->device, rdr->device)) //we have a match to another slot with same device name { rdr->device[pos] = save; //restore character Sc8in1_SetSlotForReader(rdr); if(rdr->crdr_data) { reader->crdr_data = rdr->crdr_data; reader_config_exists = 1; rdr_log_dbg(reader, D_DEVICE, "Sc8in1_InitLocks: Found config for %s", reader->device); } } else { rdr->device[pos] = save; //restore character } if(reader_config_exists) { break; } } } if(!reader_config_exists) { rdr_log_dbg(reader, D_DEVICE, "Sc8in1_InitLocks: Creating new config for %s", reader->device); // Create SC8in1_Config for reader if(cs_malloc(&reader->crdr_data, sizeof(struct sc8in1_data))) { struct sc8in1_data *crdr_data = reader->crdr_data; char *buff = NULL, *buff2 = NULL; if(cs_malloc(&buff, 128)) { snprintf(buff, 128, "sc8in1_lock_%.64s", reader->device); } if(cs_malloc(&buff2, 128)) { snprintf(buff2, 128, "display_sc8in1_lock_%.64s", reader->device); } cs_lock_create(__func__, &crdr_data->sc8in1_lock, ESTR(buff), 40000); cs_lock_create(__func__, &crdr_data->sc8in1_display_lock, ESTR(buff2), 10000); } else { reader->device[pos] = savePos; rdr_log(reader, "sc8in1: Out of memory."); return ERROR; } } reader->device[pos] = savePos; return OK; } static void sc8in1_lock(struct s_reader *reader) { struct sc8in1_data *crdr_data = reader->crdr_data; cs_writelock(__func__, &crdr_data->sc8in1_lock); rdr_log_dbg(reader, D_ATR, "Locked for access of slot %i", reader->slot); Sc8in1_Selectslot(reader, reader->slot); } static void sc8in1_unlock(struct s_reader *reader) { struct sc8in1_data *crdr_data = reader->crdr_data; cs_writeunlock(__func__, &crdr_data->sc8in1_lock); rdr_log_dbg(reader, D_ATR, "Unlocked for access of slot %i", reader->slot); } static void sc8in1_display(struct s_reader *reader, char *message) { struct sc8in1_data *crdr_data = reader->crdr_data; if(!crdr_data->mcr_type) { return; } char msg[4] = " "; if(cs_strlen(message) >= 3) { msg[0] = message[0]; msg[1] = message[1]; msg[2] = message[2]; } char text[5] = { 'S', (char)reader->slot + 0x30, msg[0], msg[1], msg[2] }; MCR_DisplayText(reader, text, sizeof(text), 400, 0); } static int32_t sc8in1_init(struct s_reader *reader) { struct sc8in1_data *crdr_data = reader->crdr_data; cs_writelock(__func__, &crdr_data->sc8in1_lock); if(reader->handle != 0) //this reader is already initialized { rdr_log_dbg(reader, D_DEVICE, "%s Sc8in1 already open", __func__); cs_writeunlock(__func__, &crdr_data->sc8in1_lock); return OK; } //get physical device name int32_t pos = cs_strlen(reader->device) - 2; //this is where : should be located; is also valid length of physical device name if(pos <= 0 || reader->device[pos] != 0x3a) //0x3a = ":" { rdr_log(reader, "ERROR: '%c' detected instead of slot separator `:` at second to last position of device %s", reader->device[pos], reader->device); } // Check if serial port is open already reader->handle = Sc8in1_GetActiveHandle(reader, 0); if(!reader->handle) { rdr_log_dbg(reader, D_DEVICE, "%s opening SC8in1", __func__); //open physical device char deviceName[128]; cs_strncpy(deviceName, reader->device, 128); deviceName[pos] = 0; reader->handle = open(deviceName, O_RDWR | O_NOCTTY | O_NONBLOCK); if(reader->handle < 0) { rdr_log(reader, "ERROR: Opening device %s with real device %s (errno=%d %s)", reader->device, deviceName, errno, strerror(errno)); reader->handle = 0; cs_writeunlock(__func__, &crdr_data->sc8in1_lock); return ERROR; } } else { // serial port already initialized rdr_log_dbg(reader, D_DEVICE, "%s another Sc8in1 already open", __func__); cs_writeunlock(__func__, &crdr_data->sc8in1_lock); return OK; } if(Phoenix_Init(reader)) { rdr_log(reader, "ERROR: Phoenix_Init returns error"); Phoenix_Close(reader); cs_writeunlock(__func__, &crdr_data->sc8in1_lock); return ERROR; } int32_t ret = Sc8in1_Init(reader); cs_writeunlock(__func__, &crdr_data->sc8in1_lock); if(ret) { rdr_log(reader, "ERROR: Sc8in1_Init returns error"); return ERROR; } return OK; } static int32_t sc8in1_close(struct s_reader *reader) { struct sc8in1_data *crdr_data = reader->crdr_data; cs_writelock(__func__, &crdr_data->sc8in1_lock); int32_t retval = Sc8in1_Close(reader); cs_writeunlock(__func__, &crdr_data->sc8in1_lock); if(retval == ERROR) { return ERROR; } return OK; } static int32_t sc8in1_get_status(struct s_reader *reader, int32_t *in) { struct sc8in1_data *crdr_data = reader->crdr_data; cs_writelock(__func__, &crdr_data->sc8in1_lock); int32_t ret = Sc8in1_GetStatus(reader, in); cs_writeunlock(__func__, &crdr_data->sc8in1_lock); return ret; } static int32_t sc8in1_activate(struct s_reader *reader, struct s_ATR *atr) { const struct s_cardreader *crdr_ops = reader->crdr; if (!crdr_ops) return ERROR; crdr_ops->lock(reader); int32_t retval = Phoenix_Reset(reader, atr); crdr_ops->unlock(reader); if(retval == ERROR) { rdr_log_dbg(reader, D_TRACE, "ERROR: Phoenix_Reset returns error"); return ERROR; } return OK; } static int32_t sc8in1_set_baudrate(struct s_reader *reader, uint32_t baudrate) { call(Sc8in1_SetBaudrate(reader, baudrate, NULL, 0)); return OK; } const struct s_cardreader cardreader_sc8in1 = { .desc = "sc8in1", .typ = R_SC8in1, .flush = 1, .read_written = 1, .need_inverse = 1, .skip_t1_command_retries = 1, .lock_init = Sc8in1_InitLocks, .lock = sc8in1_lock, .unlock = sc8in1_unlock, .display_msg = sc8in1_display, .reader_init = sc8in1_init, .close = sc8in1_close, .get_status = sc8in1_get_status, .activate = sc8in1_activate, .transmit = IO_Serial_Transmit, .receive = IO_Serial_Receive, .set_parity = IO_Serial_SetParity, .set_baudrate = sc8in1_set_baudrate, }; #endif