1282 lines
34 KiB
C
1282 lines
34 KiB
C
/*
|
|
*
|
|
* 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
|