/*
*/
/**
*
* @author Sebastien Riou
*/
package uk.co.nimp.scard;
import com.atolsystems.atolutilities.MutableInteger;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.smartcardio.CardException;
import javax.smartcardio.CardNotPresentException;
import uk.co.nimp.scard.log.ScardPrintStreamLogHandler;
import uk.co.nimp.smartcard.Apdu;
import uk.co.nimp.smartcard.UnexpectedCardResponseException;
import static uk.co.nimp.scard.Star265TerminalManager.*;
class Star265Terminal extends GenericContactTerminal {
final protected int couplerId;
//int vddMillivolts = 5000;
//int clkHertz = MAX_CLOCK_FOR_16CYCLES_PER_ETU;
final static public int DEFAULT_FREQUENCY=MAX_CLOCK_FOR_16CYCLES_PER_ETU;
final static public int DEFAULT_VOLTAGE=5000;
int stoppedClkHertz=CLOCK_RUNNING;
Star265Terminal(int couplerId, String name, boolean is260) {
super(name);
this.couplerId = couplerId;
this.is260=is260;
}
@Override
public boolean isCardPresent() throws ScardException {
return Star265TerminalManager.SCardIsPresent(couplerId) == GenericTerminal.State.CARD_PRESENT;
}
private boolean waitForCard(boolean wantPresent, long timeout) throws ScardException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout must not be negative");
}
long start = System.currentTimeMillis();
boolean exit = false;
boolean out = false;
do {
if (wantPresent) {
if (Star265TerminalManager.SCardIsPresent(couplerId) == GenericTerminal.State.CARD_PRESENT) {
exit = true;
out = true;
}
} else {
if (Star265TerminalManager.SCardIsPresent(couplerId) == GenericTerminal.State.CARD_ABSENT) {
exit = true;
out = true;
}
}
if ((0 != timeout) && (System.currentTimeMillis() - start < timeout)) {
exit = true;
}
} while (false == exit);
return out;
}
@Override
public boolean waitForCardPresent(long timeout) throws ScardException {
return waitForCard(true, timeout);
}
@Override
public boolean waitForCardAbsent(long timeout) throws ScardException {
return waitForCard(false, timeout);
}
int curCyclePerEtu=372;
boolean is260=false;
@Override
public void connectImpl(int protocol, int activation) throws ScardException, CardNotPresentException {
try {
//atr = SCardConnect(couplerId, vddMillivolts, clkHertz, protocol);//Overrun occurs during card data read
//atr = SCardConnect(couplerId, vddMillivolts, clkHertz, PROTOCOL_T_0);//Overrun occurs during card data read
//atr = SCardConnect(couplerId, vddMillivolts, 5000000, protocol);//Overrun occurs during card data read
//atr = SCardConnect(couplerId, vddMillivolts, MAX_CLOCK_FOR_16CYCLES_PER_ETU, protocol);//ok
//atr = SCardConnect(couplerId, vddMillivolts, 4000000, PROTOCOL_T_0);//ok
//atr = SCardConnect(couplerId, 5000, 4000000, PROTOCOL_T_0);//ok
if (GenericTerminal.ACTIVATION_FORCE_COLD == activation) {
SCardDisconnect(couplerId);
}
int millivolts;
if(null==vddMillivolts)
millivolts=DEFAULT_VOLTAGE;
else
millivolts=vddMillivolts;
int hertz;
if(null==clkHertz)
hertz=DEFAULT_FREQUENCY;
else
hertz=clkHertz;
MutableInteger i=new MutableInteger();
this.logLine(ScardLogHandler.LOG_INFO, "INFO: Setting Vdd to "+millivolts+"mV");
this.logLine(ScardLogHandler.LOG_INFO, "INFO: Setting Frequency to "+hertz+"Hz");
atr = SCardConnect(couplerId, millivolts, hertz, protocol, i);
vddMillivolts=millivolts;
clkHertz=hertz;
curCyclePerEtu=i.value;
logAtr(atr);
if(is260){
if((16==curCyclePerEtu) & clkHertz>MAX_CLOCK_FOR_16CYCLES_PER_ETU){
this.logLine(ScardLogHandler.LOG_WARNING, "WARNING: Frequency directive is "+clkHertz+" but is limited to "+MAX_CLOCK_FOR_16CYCLES_PER_ETU+" to avoid com overrun of STAR260");
clkHertz=MAX_CLOCK_FOR_16CYCLES_PER_ETU;
atr= SCardConnect(couplerId, vddMillivolts, clkHertz, protocol, i);
logAtr(atr);
}
}
this.logLine(ScardLogHandler.LOG_INFO, "INFO: Connection established at "+curCyclePerEtu+" cycles/ETU.");
//TODO: retrieve the actual protocol
this.protocol = PROTOCOL_T_0;
//basicChannel = new ChannelImpl(this, 0);
state = State.CARD_PRESENT;
} catch (MP300Exception e) {
if (e.code == MP300Exception.CRET_ABSENT) {
throw new CardNotPresentException("No card present", e);
} else {
throw new ScardException("connect() failed", e);
}
}
}
@Override
protected void disconnectImpl() throws ScardException {
curCyclePerEtu=372;
if (GenericTerminal.State.CARD_PRESENT != state) {
return;
}
SCardDisconnect(couplerId);
}
@Override
protected void forceDisconnectImpl() throws ScardException {
curCyclePerEtu=372;
if (GenericTerminal.State.CARD_PRESENT != state) {
return;
}
ScardForceDisconnection(couplerId);
}
protected int dllTimeoutS=0;
@Override
public void setApduTimeout(int timeoutMs)throws ScardException{
if(timeoutMs<0)
throw new IllegalArgumentException("timeoutNs<0");
this.dllTimeoutS=timeoutMs/1000;
SCardSetDllTimeout(dllTimeoutS);
}
@Override
public int getApduTimeout(){
return dllTimeoutS*1000;
}
@Override
public void sendApduImpl(Apdu apdu) throws ScardException, UnexpectedCardResponseException {
int tpduHeader = (0xFF & apdu.getCla());
tpduHeader = (tpduHeader << 8) + (0xFF & apdu.getIns());
tpduHeader = (tpduHeader << 8) + (0xFF & apdu.getP1());
tpduHeader = (tpduHeader << 8) + (0xFF & apdu.getP2());
Apdu.CardResponse response = SCardTransmit(couplerId, tpduHeader, apdu.getLcDataAsBytes(), apdu.getExpectedLe());
apdu.setResponse(response);
}
static final int CLOCK_RUNNING=-1;
@Override
public void setFrequency(int hertz) throws ScardException{
if(CLOCK_RUNNING!=stoppedClkHertz){
SCardClkRestore(couplerId);
}
if((null==clkHertz)||(clkHertz!=hertz)){
//setFrequencyImpl(hertz);
stoppedClkHertz=CLOCK_RUNNING;
//To avoid overrun
if(is260 & (16==curCyclePerEtu) & (hertz>MAX_CLOCK_FOR_16CYCLES_PER_ETU)){
this.logLine(ScardLogHandler.LOG_WARNING, "WARNING: Frequency directive is "+hertz+" but is limited to "+MAX_CLOCK_FOR_16CYCLES_PER_ETU+" to avoid com overrun of STAR260");
hertz=MAX_CLOCK_FOR_16CYCLES_PER_ETU;
}else{
this.logLine(ScardLogHandler.LOG_INFO, "INFO: Setting frequency to "+hertz+"Hz");
}
SCardSetClk(couplerId, hertz);
clkHertz=hertz;
}
}
@Override
protected void InitSetClkPinCapabilities(){
setClkPinCapabilities=new SetPinCapabilities(true,false,true);
}
@Override
public void setClkPinImpl(boolean high) throws ScardException {
if (high) {
handleUnsupportedOperation();
//throw new UnsupportedOperationException("Star265 terminal cannot fix the clock to high level mode");
//SCardClkStop(couplerId, Win32Stcp250Dll.HIGH);
} else {
SCardClkStop(couplerId, Win32Stcp250Dll.LOW);
}
stoppedClkHertz=clkHertz;
clkHertz=0;
}
@Override
public void setVoltageImpl(int millivolts) throws ScardException {
SCardSetVdd(couplerId, millivolts);
}
public static void main(String[] args) throws ScardException, CardException, IOException {
try {
Star265TerminalManager manager = new Star265TerminalManager();
List<GenericTerminal> terminals = manager.list();
terminals = manager.list();
terminals = manager.list();
if (0 == terminals.size()) {
System.out.println("Star265 terminal not detected.");
return;
}
//Star265Terminal star265 = (Star265Terminal) terminals.get(0);
GenericTerminal star265 = terminals.get(0);
terminals = manager.list();
star265.addLogHandler(new ScardPrintStreamLogHandler(System.out));
System.out.println("Please insert a card in terminal " + star265.getName());
while (false == star265.isCardPresent()) {
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
Logger.getLogger(Star265Terminal.class.getName()).log(Level.SEVERE, null, ex);
}
}
System.out.println("Try to coldConnect to " + star265.getName());
star265.coldConnect();
Apdu apdu = new Apdu(0x00, 0x8A, 0x00, 0x44, 0x02);
star265.sendApdu(apdu);
} catch (ScardException ex) {
Logger.getLogger(Star265Terminal.class.getName()).log(Level.SEVERE, null, ex);
} catch (CardException ex) {
Logger.getLogger(Star265Terminal.class.getName()).log(Level.SEVERE, null, ex);
}
}
}