578 lines
12 KiB
C
578 lines
12 KiB
C
|
|
/*
|
||
|
|
ifd_stinger.c
|
||
|
|
This module provides IFD handling functions for Stinger USB.
|
||
|
|
Usage:
|
||
|
|
|
||
|
|
Stinger Dedicated parameters.
|
||
|
|
cardmhz = real card clock (see smartcard docs)
|
||
|
|
mhz = stinger card clock (343, 400, 480, 600, 800, 1200)
|
||
|
|
detect = Inverted CTS
|
||
|
|
|
||
|
|
Ex.
|
||
|
|
[reader]
|
||
|
|
label = stinger_UP
|
||
|
|
protocol = stinger
|
||
|
|
detect = !cts
|
||
|
|
device = /dev/ttyUSB4
|
||
|
|
group = 2
|
||
|
|
cardmhz = 357
|
||
|
|
mhz = 600
|
||
|
|
emmcache = 1,3,2
|
||
|
|
services = services2
|
||
|
|
...
|
||
|
|
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "../globals.h"
|
||
|
|
|
||
|
|
#ifdef CARDREADER_STINGER
|
||
|
|
|
||
|
|
#include "../oscam-time.h"
|
||
|
|
#include "icc_async.h"
|
||
|
|
#include "ifd_db2com.h"
|
||
|
|
#include "ifd_phoenix.h"
|
||
|
|
#include "io_serial.h"
|
||
|
|
|
||
|
|
#define OK 0
|
||
|
|
#define ERROR 1
|
||
|
|
#define STINGER_DELAY 150
|
||
|
|
|
||
|
|
static int32_t Stinger_Set_Clock(struct s_reader *reader, unsigned int c);
|
||
|
|
|
||
|
|
static bool Stinger_IO_Serial_WaitToWrite(struct s_reader *reader, uint32_t delay_us, uint32_t timeout_us)
|
||
|
|
{
|
||
|
|
struct pollfd ufds;
|
||
|
|
struct timeb start, end;
|
||
|
|
int32_t ret_val;
|
||
|
|
int32_t out_fd;
|
||
|
|
int64_t polltimeout = timeout_us / 1000;
|
||
|
|
|
||
|
|
#if !defined(WITH_COOLAPI) && !defined(WITH_COOLAPI2) && !defined(WITH_AZBOX)
|
||
|
|
|
||
|
|
if(reader->typ == R_INTERNAL) { return OK; } // needed for internal readers, otherwise error!
|
||
|
|
#endif
|
||
|
|
if(delay_us > 0)
|
||
|
|
{ cs_sleepus(delay_us); } // wait in us
|
||
|
|
out_fd = reader->handle;
|
||
|
|
|
||
|
|
ufds.fd = out_fd;
|
||
|
|
ufds.events = POLLOUT;
|
||
|
|
ufds.revents = 0x0000;
|
||
|
|
cs_ftime(&start); // register start time
|
||
|
|
while(1)
|
||
|
|
{
|
||
|
|
ret_val = poll(&ufds, 1, polltimeout);
|
||
|
|
cs_ftime(&end); // register end time
|
||
|
|
switch(ret_val)
|
||
|
|
{
|
||
|
|
case 0:
|
||
|
|
rdr_log(reader, "ERROR: not ready to write, timeout=%"PRId64" ms", comp_timeb(&end, &start));
|
||
|
|
return ERROR;
|
||
|
|
case -1:
|
||
|
|
if(errno == EINTR || errno == EAGAIN)
|
||
|
|
{
|
||
|
|
cs_sleepus(1);
|
||
|
|
if(timeout_us > 0)
|
||
|
|
{
|
||
|
|
polltimeout = (timeout_us / 1000) - comp_timeb(&end, &start);
|
||
|
|
if(polltimeout < 0) { polltimeout = 0; }
|
||
|
|
}
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
rdr_log(reader, "ERROR: %s: timeout=%"PRId64" ms (errno=%d %s)", __func__, comp_timeb(&end, &start), errno, strerror(errno));
|
||
|
|
return ERROR;
|
||
|
|
default:
|
||
|
|
if(((ufds.revents) & POLLOUT) == POLLOUT)
|
||
|
|
{ return OK; }
|
||
|
|
else
|
||
|
|
{ return ERROR; }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
bool Stinger_IO_Serial_Write(struct s_reader *reader, uint32_t delay, uint32_t timeout, uint32_t size, const unsigned char *data)
|
||
|
|
{
|
||
|
|
const struct s_cardreader *crdr_ops = reader->crdr;
|
||
|
|
if (!crdr_ops) return ERROR;
|
||
|
|
|
||
|
|
if(timeout == 0) // General fix for readers not communicating timeout and delay
|
||
|
|
{
|
||
|
|
if(reader->char_delay != 0) { timeout = reader->char_delay; }
|
||
|
|
else { timeout = 1000000; }
|
||
|
|
rdr_log_dbg(reader, D_DEVICE, "Warning: write timeout 0 changed to %d us", timeout);
|
||
|
|
}
|
||
|
|
uint32_t count, to_send, i_w;
|
||
|
|
unsigned char data_w[MAX_ECM_SIZE];
|
||
|
|
|
||
|
|
to_send = (delay ? 1 : size); // calculate chars to send at one
|
||
|
|
rdr_log_dbg(reader, D_DEVICE, "Write timeout %d us, write delay %d us, to send %d char(s), chunksize %d char(s)", timeout, delay, size, to_send);
|
||
|
|
|
||
|
|
for(count = 0; count < size; count += to_send)
|
||
|
|
{
|
||
|
|
if(count + to_send > size)
|
||
|
|
{
|
||
|
|
to_send = size - count;
|
||
|
|
}
|
||
|
|
uint16_t errorcount = 0, to_do = to_send;
|
||
|
|
for(i_w = 0; i_w < to_send; i_w++)
|
||
|
|
{ data_w [i_w] = data [count + i_w]; }
|
||
|
|
rdr_log_dump_dbg(reader, D_DEVICE, data_w + (to_send - to_do), to_do, "Sending:");
|
||
|
|
AGAIN:
|
||
|
|
if(!Stinger_IO_Serial_WaitToWrite(reader, delay, timeout))
|
||
|
|
{
|
||
|
|
while(to_do != 0)
|
||
|
|
{
|
||
|
|
int32_t u = write(reader->handle, data_w + (to_send - to_do), to_do);
|
||
|
|
if(u < 1)
|
||
|
|
{
|
||
|
|
if(errno == EINTR) { continue; } //try again in case of Interrupted system call
|
||
|
|
if(errno == EAGAIN) { goto AGAIN; } //EAGAIN needs a select procedure again
|
||
|
|
errorcount++;
|
||
|
|
int16_t written = count + to_send - to_do;
|
||
|
|
if(u != 0)
|
||
|
|
{
|
||
|
|
rdr_log(reader, "ERROR: %s: Written=%d of %d (errno=%d %s)",
|
||
|
|
__func__, written , size, errno, strerror(errno));
|
||
|
|
}
|
||
|
|
if(errorcount > 10) //exit if more than 10 errors
|
||
|
|
{
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
to_do -= u;
|
||
|
|
errorcount = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
rdr_log(reader, "Timeout in Stinger_IO_Serial_WaitToWrite, delay=%d us, timeout=%d us", delay, timeout);
|
||
|
|
if(crdr_ops->read_written && reader->written > 0) // these readers need to read all transmitted chars before they can receive!
|
||
|
|
{
|
||
|
|
unsigned char buf[256];
|
||
|
|
rdr_log_dbg(reader, D_DEVICE, "Reading %d echoed transmitted chars...", reader->written);
|
||
|
|
int32_t n = reader->written;
|
||
|
|
if(IO_Serial_Read(reader, 0, 9990000, n, buf)) // use 9990000 = aprox 10 seconds (since written chars could be hughe!)
|
||
|
|
{ return ERROR; }
|
||
|
|
reader->written = 0;
|
||
|
|
rdr_log_dbg(reader, D_DEVICE, "Reading of echoed transmitted chars done!");
|
||
|
|
}
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static int32_t Stinger_Get_Info(struct s_reader *reader, unsigned char *sc, unsigned char *parity, unsigned int *baudrate)
|
||
|
|
{
|
||
|
|
unsigned char buf[64];
|
||
|
|
memset(buf, 0, 64);
|
||
|
|
|
||
|
|
//DTR down
|
||
|
|
IO_Serial_DTR_Set(reader);
|
||
|
|
buf[0] = 0;
|
||
|
|
|
||
|
|
Stinger_IO_Serial_Write(reader, 0, 10, 1, buf);
|
||
|
|
int32_t n = 0;
|
||
|
|
while(n < 32 && !IO_Serial_Read(reader, 0, ATR_TIMEOUT, 1, buf + n))
|
||
|
|
{ n++; }
|
||
|
|
|
||
|
|
//DTR up
|
||
|
|
IO_Serial_DTR_Clr(reader);
|
||
|
|
|
||
|
|
*sc = 0xFF;
|
||
|
|
*baudrate = 0xFF;
|
||
|
|
*parity = 0xFF;
|
||
|
|
|
||
|
|
|
||
|
|
if(n == 0)
|
||
|
|
{
|
||
|
|
rdr_log(reader, "Stinger_Get_Info: n %d", n);
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
if(buf[0] == 0x00)
|
||
|
|
|
||
|
|
{
|
||
|
|
/* Reader */
|
||
|
|
if((buf[1] == 0x01) || (buf[1] == 0x02))
|
||
|
|
{
|
||
|
|
*sc = buf[1] - 1;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
rdr_log(reader, "Stinger_Get_Info: buf[1] %d", buf[1]);
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
/* Baudrate */
|
||
|
|
if((buf[25] == 0x00) || (buf[25] == 0x01) || (buf[25] == 0x02))
|
||
|
|
{
|
||
|
|
*baudrate = buf[25];
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
rdr_log(reader, "Stinger_Get_Info: buf[25] %d", buf[25]);
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
/* Parity */
|
||
|
|
if((buf[26] == 0x00) || (buf[26] == 0x01) || (buf[26] == 0x02))
|
||
|
|
{
|
||
|
|
*parity = buf[26];
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
rdr_log(reader, "Stinger_Get_Info: buf[26] %d", buf[26]);
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
return OK;
|
||
|
|
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
rdr_log(reader, "Stinger_Get_Info: buf[0] %d", buf[0]);
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t Stinger_Init(struct s_reader *reader)
|
||
|
|
{
|
||
|
|
// First set card in reset state, to not change any parameters while communication ongoing
|
||
|
|
IO_Serial_RTS_Set(reader);
|
||
|
|
|
||
|
|
const struct s_cardreader *crdr_ops = reader->crdr;
|
||
|
|
if (!crdr_ops) return ERROR;
|
||
|
|
|
||
|
|
if(crdr_ops->flush) { IO_Serial_Flush(reader); }
|
||
|
|
|
||
|
|
rdr_log_dbg(reader, D_IFD, "Initializing reader type=%d", reader->typ);
|
||
|
|
|
||
|
|
/* Default serial port settings */
|
||
|
|
if(reader->atr[0] == 0)
|
||
|
|
{
|
||
|
|
if(IO_Serial_SetParams(reader, DEFAULT_BAUDRATE, 8, PARITY_NONE, 2, NULL, NULL)) { return ERROR; }
|
||
|
|
if(crdr_ops->flush) { IO_Serial_Flush(reader); }
|
||
|
|
}
|
||
|
|
|
||
|
|
return OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
int32_t Stinger_Reset(struct s_reader *reader, ATR *atr)
|
||
|
|
{
|
||
|
|
rdr_log_dbg(reader, D_IFD, "Resetting card");
|
||
|
|
int32_t ret;
|
||
|
|
int32_t i;
|
||
|
|
unsigned char buf[ATR_MAX_SIZE];
|
||
|
|
|
||
|
|
IO_Serial_SetParams(reader, DEFAULT_BAUDRATE, 8, PARITY_NONE, 2, NULL, NULL);
|
||
|
|
|
||
|
|
const struct s_cardreader *crdr_ops = reader->crdr;
|
||
|
|
if (!crdr_ops) return ERROR;
|
||
|
|
|
||
|
|
for(i = 0; i < 1; i++)
|
||
|
|
{
|
||
|
|
if(crdr_ops->flush) { IO_Serial_Flush(reader); }
|
||
|
|
|
||
|
|
|
||
|
|
ret = ERROR;
|
||
|
|
|
||
|
|
IO_Serial_Ioctl_Lock(reader, 1);
|
||
|
|
|
||
|
|
IO_Serial_RTS_Set(reader);
|
||
|
|
|
||
|
|
cs_sleepms(50);
|
||
|
|
|
||
|
|
IO_Serial_RTS_Clr(reader);
|
||
|
|
|
||
|
|
cs_sleepms(50);
|
||
|
|
|
||
|
|
IO_Serial_Ioctl_Lock(reader, 0);
|
||
|
|
|
||
|
|
int32_t n = 0;
|
||
|
|
|
||
|
|
while(n < 1 && !IO_Serial_Read(reader, 0, ATR_TIMEOUT, 1, buf + n))
|
||
|
|
{ n++; }
|
||
|
|
|
||
|
|
while(n < ATR_MAX_SIZE && !IO_Serial_Read(reader, 0, 900000, 1, buf + n))
|
||
|
|
{ n++; }
|
||
|
|
|
||
|
|
|
||
|
|
if(n == 0)
|
||
|
|
{ continue; }
|
||
|
|
if(ATR_InitFromArray(atr, buf, n) != ERROR)
|
||
|
|
{ ret = OK; }
|
||
|
|
// Successfully retrieve ATR
|
||
|
|
if(ret == OK)
|
||
|
|
{ break; }
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
if(ret == OK)
|
||
|
|
{
|
||
|
|
IO_Serial_SetParams(reader, 115200, 8, PARITY_NONE, 1/*2*/, NULL, NULL);
|
||
|
|
return OK;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{ return ret; }
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t stinger_mouse_init(struct s_reader *reader)
|
||
|
|
{
|
||
|
|
|
||
|
|
|
||
|
|
unsigned int clock_mhz = 0;
|
||
|
|
|
||
|
|
const struct s_cardreader *crdr_ops = reader->crdr;
|
||
|
|
if (!crdr_ops) return ERROR;
|
||
|
|
|
||
|
|
if(detect_db2com_reader(reader))
|
||
|
|
{
|
||
|
|
reader->crdr = crdr_ops = &cardreader_db2com;
|
||
|
|
return crdr_ops->reader_init(reader);
|
||
|
|
}
|
||
|
|
|
||
|
|
clock_mhz = reader->mhz;
|
||
|
|
reader->mhz = reader->cardmhz;
|
||
|
|
|
||
|
|
reader->handle = open(reader->device, O_RDWR | O_NOCTTY | O_NONBLOCK);
|
||
|
|
if(reader->handle < 0)
|
||
|
|
{
|
||
|
|
rdr_log(reader, "ERROR: Opening device %s (errno=%d %s)",
|
||
|
|
reader->device, errno, strerror(errno));
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
if(Stinger_Init(reader))
|
||
|
|
{
|
||
|
|
rdr_log(reader, "ERROR: Stinger_Init returns error");
|
||
|
|
Phoenix_Close(reader);
|
||
|
|
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
// SET CLOCK
|
||
|
|
|
||
|
|
cs_sleepms(200);
|
||
|
|
Stinger_Set_Clock(reader, clock_mhz);
|
||
|
|
|
||
|
|
return OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
int32_t Stinger_IO_Serial_SetBaudrate(struct s_reader *reader, uint32_t baudrate)
|
||
|
|
{
|
||
|
|
unsigned char dat[8];
|
||
|
|
unsigned char p = 0xFF;
|
||
|
|
unsigned int b = 0xFF;
|
||
|
|
unsigned char s = 0xFF;
|
||
|
|
memset(dat, 0, 8);
|
||
|
|
rdr_log_dbg(reader, D_IFD, "Setting baudrate to %u", baudrate);
|
||
|
|
|
||
|
|
if(baudrate == 9600)
|
||
|
|
{
|
||
|
|
dat[2] = 0;
|
||
|
|
}
|
||
|
|
else if(baudrate == 38400)
|
||
|
|
{
|
||
|
|
dat[2] = 1;
|
||
|
|
}
|
||
|
|
else if(baudrate == 115200)
|
||
|
|
{
|
||
|
|
dat[2] = 2;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
rdr_log_dbg(reader, D_IFD, "Baudrate value not supported");
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
Stinger_Get_Info(reader, &s, &p, &b);
|
||
|
|
|
||
|
|
|
||
|
|
if((s == 0xFF) || (p == 0xFF) || (b == 0xFF))
|
||
|
|
{
|
||
|
|
rdr_log_dbg(reader, D_IFD, "Stinger_Get_Info error");
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
//Stinger cmds
|
||
|
|
dat[0] = 0x03;
|
||
|
|
dat[1] = s;
|
||
|
|
dat[3] = p;
|
||
|
|
|
||
|
|
|
||
|
|
//DTR down
|
||
|
|
IO_Serial_DTR_Set(reader);
|
||
|
|
|
||
|
|
Stinger_IO_Serial_Write(reader, 0, 10, 4, dat);
|
||
|
|
|
||
|
|
|
||
|
|
int32_t n = 0;
|
||
|
|
while(n < 2 && !IO_Serial_Read(reader, 0, 300000, 1, dat + n))
|
||
|
|
{ n++; }
|
||
|
|
|
||
|
|
|
||
|
|
//DTR up
|
||
|
|
IO_Serial_DTR_Clr(reader);
|
||
|
|
|
||
|
|
reader->current_baudrate = baudrate; //so if update fails, reader->current_baudrate is not changed either
|
||
|
|
|
||
|
|
|
||
|
|
return OK;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t Stinger_IO_Serial_SetParity(struct s_reader *reader, unsigned char parity)
|
||
|
|
{
|
||
|
|
|
||
|
|
//Stinger cmds
|
||
|
|
unsigned char dat[4];
|
||
|
|
unsigned char p = 0xFF;
|
||
|
|
unsigned int b = 0xFF;
|
||
|
|
unsigned char s = 0xFF;
|
||
|
|
|
||
|
|
if(parity == PARITY_NONE)
|
||
|
|
{
|
||
|
|
dat[3] = 0;
|
||
|
|
}
|
||
|
|
else if(parity == PARITY_ODD)
|
||
|
|
{
|
||
|
|
dat[3] = 1;
|
||
|
|
}
|
||
|
|
else if(parity == PARITY_EVEN)
|
||
|
|
{
|
||
|
|
dat[3] = 2;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
rdr_log_dbg(reader, D_IFD, "Parity value not supported");
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
Stinger_Get_Info(reader, &s, &p, &b);
|
||
|
|
|
||
|
|
if((s == 0xFF) || (p == 0xFF) || (b == 0xFF))
|
||
|
|
{
|
||
|
|
rdr_log_dbg(reader, D_IFD, "Stinger_Get_Info error");
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
//Stinger cmd
|
||
|
|
dat[0] = 0x03;
|
||
|
|
dat[1] = s;
|
||
|
|
dat[2] = b;
|
||
|
|
|
||
|
|
|
||
|
|
//DTR down
|
||
|
|
IO_Serial_DTR_Set(reader);
|
||
|
|
|
||
|
|
Stinger_IO_Serial_Write(reader, 0, 10, 4, dat);
|
||
|
|
|
||
|
|
int32_t n = 0;
|
||
|
|
while(n < 2 && !IO_Serial_Read(reader, 0, 300000, 1, dat + n))
|
||
|
|
{ n++; }
|
||
|
|
|
||
|
|
//DTR up
|
||
|
|
IO_Serial_DTR_Clr(reader);
|
||
|
|
|
||
|
|
return OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
static int32_t Stinger_Set_Clock(struct s_reader *reader, unsigned int c)
|
||
|
|
{
|
||
|
|
rdr_log_dbg(reader, D_IFD, "Setting Smartcard clock at: %d", c);
|
||
|
|
unsigned char p = 0xFF;
|
||
|
|
unsigned int b = 0xFF;
|
||
|
|
unsigned char s = 0xFF;
|
||
|
|
unsigned char dat[3];
|
||
|
|
|
||
|
|
Stinger_Get_Info(reader, &s, &p, &b);
|
||
|
|
|
||
|
|
if((s == 0xFF) || (p == 0xFF) || (b == 0xFF))
|
||
|
|
{
|
||
|
|
rdr_log_dbg(reader, D_IFD, "Stinger_Get_Info error");
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
//Stinger cmd
|
||
|
|
dat[0] = 0x02; // Set Clock COMMAND
|
||
|
|
dat[1] = s;
|
||
|
|
|
||
|
|
if(c == 343)
|
||
|
|
{
|
||
|
|
dat[2] = 1;
|
||
|
|
}
|
||
|
|
else if(c == 400)
|
||
|
|
{
|
||
|
|
dat[2] = 2;
|
||
|
|
}
|
||
|
|
else if(c == 480)
|
||
|
|
{
|
||
|
|
dat[2] = 3;
|
||
|
|
}
|
||
|
|
else if(c == 600)
|
||
|
|
{
|
||
|
|
dat[2] = 4;
|
||
|
|
}
|
||
|
|
else if(c == 800)
|
||
|
|
{
|
||
|
|
dat[2] = 5;
|
||
|
|
}
|
||
|
|
else if(c == 1200)
|
||
|
|
{
|
||
|
|
dat[2] = 6;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
rdr_log_dbg(reader, D_IFD, "Clock speed not recognized. Check configuration");
|
||
|
|
return ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
//DTR down
|
||
|
|
IO_Serial_DTR_Set(reader);
|
||
|
|
|
||
|
|
// SEND Command
|
||
|
|
Stinger_IO_Serial_Write(reader, 0, 10, 3, dat);
|
||
|
|
|
||
|
|
int32_t n = 0;
|
||
|
|
while(n < 2 && !IO_Serial_Read(reader, 0, 300000, 1, dat + n))
|
||
|
|
{ n++; }
|
||
|
|
|
||
|
|
//DTR up
|
||
|
|
IO_Serial_DTR_Clr(reader);
|
||
|
|
rdr_log_dbg(reader, D_IFD, "Smartcard clock at %d set", c);
|
||
|
|
return OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
const struct s_cardreader cardreader_stinger =
|
||
|
|
{
|
||
|
|
.desc = "stinger",
|
||
|
|
.typ = R_MOUSE,
|
||
|
|
.flush = 1,
|
||
|
|
.read_written = 1,
|
||
|
|
.need_inverse = 1,
|
||
|
|
.reader_init = stinger_mouse_init,
|
||
|
|
.get_status = Phoenix_GetStatus,
|
||
|
|
.activate = Stinger_Reset,
|
||
|
|
.transmit = IO_Serial_Transmit,
|
||
|
|
.receive = IO_Serial_Receive,
|
||
|
|
.close = Phoenix_Close,
|
||
|
|
.set_parity = Stinger_IO_Serial_SetParity,
|
||
|
|
.set_baudrate = Stinger_IO_Serial_SetBaudrate,
|
||
|
|
};
|
||
|
|
|
||
|
|
#endif
|